├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── rustfmt.toml ├── README.md ├── Cargo.toml ├── src ├── shared.rs ├── lib.rs ├── modules │ ├── sym.txt │ └── emoji.txt └── styling.rs ├── LICENSE ├── Cargo.lock └── CHANGELOG.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [typst] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .vscode 3 | .idea 4 | _things 5 | desktop.ini 6 | .DS_Store 7 | 8 | # Rust 9 | /target 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_small_heuristics = "Max" 2 | max_width = 90 3 | chain_width = 70 4 | struct_lit_width = 50 5 | use_field_init_shorthand = true 6 | merge_derives = false 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # codex 2 | [![Crates.io](https://img.shields.io/crates/v/codex.svg)](https://crates.io/crates/codex) 3 | [![Documentation](https://docs.rs/codex/badge.svg)](https://docs.rs/codex) 4 | 5 | Human-friendly notation for Unicode symbols. 6 | 7 | ## License 8 | This crate is licensed under the Apache 2.0 license. 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous integration 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | ci: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - uses: dtolnay/rust-toolchain@stable 10 | - run: cargo build 11 | - run: cargo test --features=_test-unicode-conformance 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codex" 3 | version = "0.2.0" 4 | authors = ["The Typst Project Developers"] 5 | edition = "2021" 6 | description = "Human-friendly notation for Unicode symbols." 7 | repository = "https://github.com/typst/codex" 8 | readme = "README.md" 9 | license = "Apache-2.0" 10 | categories = ["encoding", "text-processing"] 11 | keywords = ["unicode", "symbols"] 12 | 13 | [features] 14 | default = ["styling"] 15 | styling = [] 16 | _test-unicode-conformance = ["ureq"] 17 | 18 | [build-dependencies] 19 | ureq = { version = "3.0.12", optional = true } 20 | -------------------------------------------------------------------------------- /src/shared.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | /// A set of modifiers. 4 | /// 5 | /// Beware: The [`Eq`] and [`Hash`] implementations are dependent on the 6 | /// ordering of the modifiers, in opposition to what a set would usually 7 | /// constitute. To test for set-wise equality, use [`iter`](Self::iter) and 8 | /// collect into a true set type like [`HashSet`](std::collections::HashSet). 9 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 10 | pub struct ModifierSet( 11 | // Note: the visibility needs to be `pub(crate)`, since build.rs outputs 12 | // `ModifierSet(...)`. 13 | pub(crate) S, 14 | ); 15 | 16 | impl> ModifierSet { 17 | /// Constructs a modifier set from a string, where modifiers are separated 18 | /// by the character `.`. 19 | /// 20 | /// `s` should not contain any empty modifiers (i.e. it shouldn't contain 21 | /// the sequence `..`) and no modifier should occur twice. Otherwise, 22 | /// unexpected errors can occur. 23 | pub fn from_raw_dotted(s: S) -> Self { 24 | // Checking the other requirement too feels like it would be a bit too 25 | // expensive, even for debug mode. 26 | debug_assert!( 27 | !s.contains(".."), 28 | "ModifierSet::from_raw_dotted called with string containing empty modifier" 29 | ); 30 | Self(s) 31 | } 32 | 33 | /// Whether `self` is empty. 34 | pub fn is_empty(&self) -> bool { 35 | self.0.is_empty() 36 | } 37 | 38 | /// Gets the string of modifiers separated by `.`. 39 | pub fn as_str(&self) -> &str { 40 | &self.0 41 | } 42 | 43 | /// Converts the underlying string to a slice. 44 | pub fn as_deref(&self) -> ModifierSet<&str> { 45 | ModifierSet(&self.0) 46 | } 47 | 48 | /// Inserts a new modifier into the set. 49 | /// 50 | /// `m` should not be empty, contain the character `.`, or already be in the 51 | /// set. Otherwise, unexpected errors can occur. 52 | pub fn insert_raw(&mut self, m: &str) 53 | where 54 | S: for<'a> std::ops::AddAssign<&'a str>, 55 | { 56 | if !self.0.is_empty() { 57 | self.0 += "."; 58 | } 59 | self.0 += m; 60 | } 61 | 62 | /// Iterates over the list of modifiers in an arbitrary order. 63 | pub fn iter(&self) -> impl Iterator { 64 | self.into_iter() 65 | } 66 | 67 | /// Whether the set contains the modifier `m`. 68 | pub fn contains(&self, m: &str) -> bool { 69 | self.iter().any(|lhs| lhs == m) 70 | } 71 | 72 | /// Finds the best match from the list. 73 | /// 74 | /// To be considered a match, the modifier set must be a superset of (or 75 | /// equal to) `self`. Among different matches, the best one is selected by 76 | /// the following two criteria (in order): 77 | /// 1. Number of modifiers in common with `self` (more is better). 78 | /// 2. Total number of modifiers (fewer is better). 79 | /// 80 | /// If there are multiple best matches, the first of them is returned. 81 | pub fn best_match_in<'a, T>( 82 | &self, 83 | variants: impl Iterator, T)>, 84 | ) -> Option { 85 | let mut best = None; 86 | let mut best_score = None; 87 | 88 | // Find the best table entry with this name. 89 | for candidate in variants.filter(|(set, _)| self.is_subset(*set)) { 90 | let mut matching = 0; 91 | let mut total = 0; 92 | for modifier in candidate.0.iter() { 93 | if self.contains(modifier) { 94 | matching += 1; 95 | } 96 | total += 1; 97 | } 98 | 99 | let score = (matching, std::cmp::Reverse(total)); 100 | if best_score.is_none_or(|b| score > b) { 101 | best = Some(candidate.1); 102 | best_score = Some(score); 103 | } 104 | } 105 | 106 | best 107 | } 108 | 109 | /// Whether all modifiers in `self` are also present in `other`. 110 | pub fn is_subset(&self, other: ModifierSet<&str>) -> bool { 111 | self.iter().all(|m| other.contains(m)) 112 | } 113 | } 114 | 115 | impl Default for ModifierSet { 116 | /// Constructs the default modifier set. 117 | /// 118 | /// This is typically the empty set, though the remark from 119 | /// [`Self::from_raw_dotted`] applies since `S::default()` could technically 120 | /// be anything. 121 | fn default() -> Self { 122 | Self(S::default()) 123 | } 124 | } 125 | 126 | impl<'a, S: Deref> IntoIterator for &'a ModifierSet { 127 | type Item = &'a str; 128 | type IntoIter = std::str::Split<'a, char>; 129 | 130 | /// Iterate over the list of modifiers in an arbitrary order. 131 | fn into_iter(self) -> Self::IntoIter { 132 | let mut iter = self.0.split('.'); 133 | if self.0.is_empty() { 134 | // Empty the iterator 135 | let _ = iter.next(); 136 | } 137 | iter 138 | } 139 | } 140 | 141 | impl<'a> IntoIterator for ModifierSet<&'a str> { 142 | type Item = &'a str; 143 | type IntoIter = std::str::Split<'a, char>; 144 | 145 | /// Iterate over the list of modifiers in an arbitrary order. 146 | fn into_iter(self) -> Self::IntoIter { 147 | let mut iter = self.0.split('.'); 148 | if self.0.is_empty() { 149 | // Empty the iterator 150 | let _ = iter.next(); 151 | } 152 | iter 153 | } 154 | } 155 | 156 | #[cfg(test)] 157 | mod tests { 158 | type ModifierSet = super::ModifierSet<&'static str>; 159 | 160 | #[test] 161 | fn default_is_empty() { 162 | assert!(ModifierSet::default().is_empty()); 163 | } 164 | 165 | #[test] 166 | fn iter_count() { 167 | assert_eq!(ModifierSet::default().iter().count(), 0); 168 | assert_eq!(ModifierSet::from_raw_dotted("a").iter().count(), 1); 169 | assert_eq!(ModifierSet::from_raw_dotted("a.b").iter().count(), 2); 170 | assert_eq!(ModifierSet::from_raw_dotted("a.b.c").iter().count(), 3); 171 | } 172 | 173 | #[test] 174 | fn subset() { 175 | assert!(ModifierSet::from_raw_dotted("a") 176 | .is_subset(ModifierSet::from_raw_dotted("a.b"))); 177 | assert!(ModifierSet::from_raw_dotted("a") 178 | .is_subset(ModifierSet::from_raw_dotted("b.a"))); 179 | assert!(ModifierSet::from_raw_dotted("a.b") 180 | .is_subset(ModifierSet::from_raw_dotted("b.c.a"))); 181 | } 182 | 183 | #[test] 184 | fn best_match() { 185 | // 1. more modifiers in common with self 186 | assert_eq!( 187 | ModifierSet::from_raw_dotted("a.b").best_match_in( 188 | [ 189 | (ModifierSet::from_raw_dotted("a.c"), 1), 190 | (ModifierSet::from_raw_dotted("a.b"), 2), 191 | ] 192 | .into_iter() 193 | ), 194 | Some(2) 195 | ); 196 | // 2. fewer modifiers in general 197 | assert_eq!( 198 | ModifierSet::from_raw_dotted("a").best_match_in( 199 | [ 200 | (ModifierSet::from_raw_dotted("a"), 1), 201 | (ModifierSet::from_raw_dotted("a.b"), 2), 202 | ] 203 | .into_iter() 204 | ), 205 | Some(1) 206 | ); 207 | // the first rule takes priority over the second 208 | assert_eq!( 209 | ModifierSet::from_raw_dotted("a.b").best_match_in( 210 | [ 211 | (ModifierSet::from_raw_dotted("a"), 1), 212 | (ModifierSet::from_raw_dotted("a.b"), 2), 213 | ] 214 | .into_iter() 215 | ), 216 | Some(2) 217 | ); 218 | // among multiple best matches, the first one is returned 219 | assert_eq!( 220 | ModifierSet::default().best_match_in( 221 | [ 222 | (ModifierSet::from_raw_dotted("a"), 1), 223 | (ModifierSet::from_raw_dotted("b"), 2) 224 | ] 225 | .into_iter() 226 | ), 227 | Some(1) 228 | ); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "adler2" 7 | version = "2.0.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 10 | 11 | [[package]] 12 | name = "base64" 13 | version = "0.22.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 16 | 17 | [[package]] 18 | name = "bytes" 19 | version = "1.10.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 22 | 23 | [[package]] 24 | name = "cc" 25 | version = "1.2.31" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" 28 | dependencies = [ 29 | "shlex", 30 | ] 31 | 32 | [[package]] 33 | name = "cfg-if" 34 | version = "1.0.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 37 | 38 | [[package]] 39 | name = "codex" 40 | version = "0.2.0" 41 | dependencies = [ 42 | "ureq", 43 | ] 44 | 45 | [[package]] 46 | name = "crc32fast" 47 | version = "1.5.0" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" 50 | dependencies = [ 51 | "cfg-if", 52 | ] 53 | 54 | [[package]] 55 | name = "flate2" 56 | version = "1.1.2" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" 59 | dependencies = [ 60 | "crc32fast", 61 | "miniz_oxide", 62 | ] 63 | 64 | [[package]] 65 | name = "fnv" 66 | version = "1.0.7" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 69 | 70 | [[package]] 71 | name = "getrandom" 72 | version = "0.2.16" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 75 | dependencies = [ 76 | "cfg-if", 77 | "libc", 78 | "wasi", 79 | ] 80 | 81 | [[package]] 82 | name = "http" 83 | version = "1.3.1" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 86 | dependencies = [ 87 | "bytes", 88 | "fnv", 89 | "itoa", 90 | ] 91 | 92 | [[package]] 93 | name = "httparse" 94 | version = "1.10.1" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 97 | 98 | [[package]] 99 | name = "itoa" 100 | version = "1.0.15" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 103 | 104 | [[package]] 105 | name = "libc" 106 | version = "0.2.174" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 109 | 110 | [[package]] 111 | name = "log" 112 | version = "0.4.27" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 115 | 116 | [[package]] 117 | name = "miniz_oxide" 118 | version = "0.8.9" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 121 | dependencies = [ 122 | "adler2", 123 | ] 124 | 125 | [[package]] 126 | name = "once_cell" 127 | version = "1.21.3" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 130 | 131 | [[package]] 132 | name = "percent-encoding" 133 | version = "2.3.1" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 136 | 137 | [[package]] 138 | name = "ring" 139 | version = "0.17.14" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 142 | dependencies = [ 143 | "cc", 144 | "cfg-if", 145 | "getrandom", 146 | "libc", 147 | "untrusted", 148 | "windows-sys", 149 | ] 150 | 151 | [[package]] 152 | name = "rustls" 153 | version = "0.23.31" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 156 | dependencies = [ 157 | "log", 158 | "once_cell", 159 | "ring", 160 | "rustls-pki-types", 161 | "rustls-webpki", 162 | "subtle", 163 | "zeroize", 164 | ] 165 | 166 | [[package]] 167 | name = "rustls-pemfile" 168 | version = "2.2.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 171 | dependencies = [ 172 | "rustls-pki-types", 173 | ] 174 | 175 | [[package]] 176 | name = "rustls-pki-types" 177 | version = "1.12.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 180 | dependencies = [ 181 | "zeroize", 182 | ] 183 | 184 | [[package]] 185 | name = "rustls-webpki" 186 | version = "0.103.4" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" 189 | dependencies = [ 190 | "ring", 191 | "rustls-pki-types", 192 | "untrusted", 193 | ] 194 | 195 | [[package]] 196 | name = "shlex" 197 | version = "1.3.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 200 | 201 | [[package]] 202 | name = "subtle" 203 | version = "2.6.1" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 206 | 207 | [[package]] 208 | name = "untrusted" 209 | version = "0.9.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 212 | 213 | [[package]] 214 | name = "ureq" 215 | version = "3.0.12" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "9f0fde9bc91026e381155f8c67cb354bcd35260b2f4a29bcc84639f762760c39" 218 | dependencies = [ 219 | "base64", 220 | "flate2", 221 | "log", 222 | "percent-encoding", 223 | "rustls", 224 | "rustls-pemfile", 225 | "rustls-pki-types", 226 | "ureq-proto", 227 | "utf-8", 228 | "webpki-roots 0.26.11", 229 | ] 230 | 231 | [[package]] 232 | name = "ureq-proto" 233 | version = "0.4.2" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7" 236 | dependencies = [ 237 | "base64", 238 | "http", 239 | "httparse", 240 | "log", 241 | ] 242 | 243 | [[package]] 244 | name = "utf-8" 245 | version = "0.7.6" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 248 | 249 | [[package]] 250 | name = "wasi" 251 | version = "0.11.1+wasi-snapshot-preview1" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 254 | 255 | [[package]] 256 | name = "webpki-roots" 257 | version = "0.26.11" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 260 | dependencies = [ 261 | "webpki-roots 1.0.2", 262 | ] 263 | 264 | [[package]] 265 | name = "webpki-roots" 266 | version = "1.0.2" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" 269 | dependencies = [ 270 | "rustls-pki-types", 271 | ] 272 | 273 | [[package]] 274 | name = "windows-sys" 275 | version = "0.52.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 278 | dependencies = [ 279 | "windows-targets", 280 | ] 281 | 282 | [[package]] 283 | name = "windows-targets" 284 | version = "0.52.6" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 287 | dependencies = [ 288 | "windows_aarch64_gnullvm", 289 | "windows_aarch64_msvc", 290 | "windows_i686_gnu", 291 | "windows_i686_gnullvm", 292 | "windows_i686_msvc", 293 | "windows_x86_64_gnu", 294 | "windows_x86_64_gnullvm", 295 | "windows_x86_64_msvc", 296 | ] 297 | 298 | [[package]] 299 | name = "windows_aarch64_gnullvm" 300 | version = "0.52.6" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 303 | 304 | [[package]] 305 | name = "windows_aarch64_msvc" 306 | version = "0.52.6" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 309 | 310 | [[package]] 311 | name = "windows_i686_gnu" 312 | version = "0.52.6" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 315 | 316 | [[package]] 317 | name = "windows_i686_gnullvm" 318 | version = "0.52.6" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 321 | 322 | [[package]] 323 | name = "windows_i686_msvc" 324 | version = "0.52.6" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 327 | 328 | [[package]] 329 | name = "windows_x86_64_gnu" 330 | version = "0.52.6" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 333 | 334 | [[package]] 335 | name = "windows_x86_64_gnullvm" 336 | version = "0.52.6" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 339 | 340 | [[package]] 341 | name = "windows_x86_64_msvc" 342 | version = "0.52.6" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 345 | 346 | [[package]] 347 | name = "zeroize" 348 | version = "1.8.1" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 351 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | ### New in `sym` 6 | 7 | - Mathematical symbols 8 | - `subset.approx`: ⫉ 9 | - `subset.closed`: ⫏ 10 | - `subset.closed.eq`: ⫑ 11 | - `subset.eq.dot`: ⫃ 12 | - `subset.equiv`: ⫅ 13 | - `subset.nequiv`: ⫋ 14 | - `subset.plus`: ⪿ 15 | - `subset.tilde`: ⫇ 16 | - `subset.times`: ⫁ 17 | - `supset.approx`: ⫊ 18 | - `supset.closed`: ⫐ 19 | - `supset.closed.eq`: ⫒ 20 | - `supset.eq.dot`: ⫄ 21 | - `supset.equiv`: ⫆ 22 | - `supset.nequiv`: ⫌ 23 | - `supset.plus`: ⫀ 24 | - `supset.tilde`: ⫈ 25 | - `supset.times`: ⫂ 26 | - `pee`: ℘ 27 | 28 | - Currency 29 | - `riyal`: ⃁ 30 | 31 | ### New in `emoji` 32 | 33 | - bigfoot: 🫈 34 | - dancing.ballet: 🧑‍🩰 35 | - face.distorted: 🫪 36 | - fightcloud: 🫯 37 | - landslide: 🛘 38 | - orca: 🫍 39 | - treasure: 🪎 40 | - trombone: 🪊 41 | 42 | ### Removals **(Breaking change)** 43 | 44 | These previously deprecated items were removed: 45 | - `paren.double.*` 46 | - `brace.double.*` 47 | - `bracket.double.*` 48 | - `shell.double.*` 49 | - `bar.circle` 50 | - `ast.small` 51 | - `ast.circle` 52 | - `backslash.circle` 53 | - `dash.circle` 54 | - `dot.circle`, `dot.circle.big` 55 | - `quote.angle.*` 56 | - `plus.circle`, `plus.circle.*` 57 | - `plus.small` 58 | - `minus.circle` 59 | - `div.circle` 60 | - `times.circle`, `times.circle.big` 61 | - `eq.circle` 62 | - `eq.small` 63 | - `gt.circle` 64 | - `gt.small` 65 | - `lt.circle` 66 | - `lt.small` 67 | - `sect`, `sect.*` 68 | - `diff` 69 | - `integral.sect` 70 | - `angle.l`, `angle.l.*` 71 | - `angle.r`, `angle.r.*` 72 | - `angle.oblique` 73 | - `angle.right.sq` 74 | - `angle.spheric.top` 75 | - `parallel.circle` 76 | - `perp.circle` 77 | - `franc` 78 | - `circle.nested` 79 | - `kai`, `Kai` 80 | - `alef` 81 | - `bet` 82 | - `gimmel` 83 | - `dalet` 84 | - `shin` 85 | - `planck.reduce` 86 | 87 | ## Version 0.2.0 (October 7, 2025) 88 | 89 | ### General changes 90 | 91 | - Codepoints that have a symbol and emoji presentation now have the correct variation selector attached depending on whether they appear in `sym` or `emoji` 92 | - Added support for multi-character symbols **(Breaking change)** 93 | - Added support for deprecated symbol variants **(Breaking change)** 94 | - Added `ModifierSet` type and made use of it in public API **(Breaking change)** 95 | - Added `Symbol::get`, `Symbol::variants`, and `Symbol::modifiers` functions 96 | - Added Rust module for styling mathematical codepoints (behind `styling` feature flag, enabled by default) 97 | 98 | ### Changed codepoint **(Breaking change)** 99 | 100 | - `sym.planck` from ℎ to ħ 101 | - `sym.peso` from ₱ to $ 102 | - `emoji.dancing.bunny` from women to gender neutral 103 | - `emoji.handholding` from men to gender neutral 104 | 105 | ### New in `sym` 106 | 107 | - Arrows 108 | - `arrow.r.double.struck`: ⤃ 109 | - `arrow.r.struck`: ⇸ 110 | - `arrow.r.dstruck`: ⇻ 111 | - `arrow.r.tail.struck`: ⤔ 112 | - `arrow.r.tail.dstruck`: ⤕ 113 | - `arrow.r.twohead.struck`: ⤀ 114 | - `arrow.r.twohead.dstruck`: ⤁ 115 | - `arrow.r.twohead.tail`: ⤖ 116 | - `arrow.r.twohead.tail.struck`: ⤗ 117 | - `arrow.r.twohead.tail.dstruck`: ⤘ 118 | - `arrow.r.open`: ⇾ 119 | - `arrow.l.double.struck`: ⤂ 120 | - `arrow.l.struck`: ⇷ 121 | - `arrow.l.dstruck`: ⇺ 122 | - `arrow.l.tail.struck`: ⬹ 123 | - `arrow.l.tail.dstruck`: ⬺ 124 | - `arrow.l.twohead.struck`: ⬴ 125 | - `arrow.l.twohead.dstruck`: ⬵ 126 | - `arrow.l.twohead.tail`: ⬻ 127 | - `arrow.l.twohead.tail.struck`: ⬼ 128 | - `arrow.l.twohead.tail.dstruck`: ⬽ 129 | - `arrow.t.struck`: ⤉ 130 | - `arrow.t.dstruck`: ⇞ 131 | - `arrow.b.struck`: ⤈ 132 | - `arrow.b.dstruck`: ⇟ 133 | - `arrow.l.r.double.struck`: ⤄ 134 | - `arrow.l.r.struck`: ⇹ 135 | - `arrow.l.r.dstruck`: ⇼ 136 | - `arrow.l.open`: ⇽ 137 | - `arrow.l.r.open`: ⇿ 138 | 139 | - Delimiters 140 | - `bracket.l.tick.t`: ⦍ 141 | - `bracket.l.tick.b`: ⦏ 142 | - `bracket.r.tick.t`: ⦐ 143 | - `bracket.r.tick.b`: ⦎ 144 | - `paren.l.flat`: ⟮ 145 | - `paren.r.flat`: ⟯ 146 | - `paren.l.closed`: ⦇ 147 | - `paren.r.closed`: ⦈ 148 | - `shell.l.filled`: ⦗ 149 | - `shell.r.filled`: ⦘ 150 | - `chevron.l.closed`: ⦉ 151 | - `chevron.r.closed`: ⦊ 152 | - `corner.l.t`: ⌜ 153 | - `corner.l.b`: ⌞ 154 | - `corner.r.t`: ⌝ 155 | - `corner.r.b`: ⌟ 156 | - `bag.l`: ⟅ 157 | - `bag.r`: ⟆ 158 | - `mustache.l`: ⎰ 159 | - `mustache.r`: ⎱ 160 | 161 | - Punctuation 162 | - `comma.inv`: ⸲ 163 | - `comma.rev`: ⹁ 164 | - `interrobang.inv`: ⸘ 165 | - `semi.inv`: ⸵ 166 | - `slash.o`: ⊘ 167 | - `ast.op.o`: ⊛ 168 | - `dot.o`: ⊙ 169 | - `dot.o.big`: ⨀ 170 | - `colon.currency`: ₡ 171 | - `permyriad`: ‱ 172 | 173 | - Arithmetic 174 | - `plus.o`: ⊕ 175 | - `plus.o.arrow`: ⟴ 176 | - `plus.o.big`: ⨁ 177 | - `plus.o.l`: ⨭ 178 | - `plus.o.r`: ⨮ 179 | - `minus.o`: ⊖ 180 | - `div.o`: ⨸ 181 | - `div.slanted.o`: ⦼ 182 | - `times.o`: ⊗ 183 | - `times.o.big`: ⨂ 184 | - `times.o.l`: ⨴ 185 | - `times.o.r`: ⨵ 186 | - `times.o.hat`: ⨶ 187 | 188 | - Function and category theory 189 | - `compose.o`: ⊚ 190 | - `convolve.o`: ⊛ 191 | 192 | - Geometry 193 | - `angle.obtuse`: ⦦ 194 | - `angle.azimuth`: ⍼ 195 | - `angle.right.arc.dot`: ⦝ 196 | - `angzarr`: ⍼ 197 | 198 | - Shapes 199 | - `bullet.op`: ∙ 200 | - `bullet.o`: ⦿ 201 | - `bullet.stroked`: ◦ 202 | - `bullet.stroked.o`: ⦾ 203 | - `bullet.hole`: ◘ 204 | - `bullet.hyph`: ⁃ 205 | - `bullet.tri`: ‣ 206 | - `bullet.l`: ⁌ 207 | - `bullet.r`: ⁍ 208 | 209 | - Miscellaneous 210 | - `cc`: 🅭 211 | - `cc.by`: 🅯 212 | - `cc.nc`: 🄏 213 | - `cc.nd`: ⊜ 214 | - `cc.public`: 🅮 215 | - `cc.sa`: 🄎 216 | - `cc.zero`: 🄍 217 | 218 | - Currency 219 | - `afghani`: ؋ 220 | - `baht`: ฿ 221 | - `cedi`: ₵ 222 | - `cent`: ¢ 223 | - `currency`: ¤ 224 | - `dong`: ₫ 225 | - `dorome`: ߾ 226 | - `dram`: ֏ 227 | - `guarani`: ₲ 228 | - `hryvnia`: ₴ 229 | - `kip`: ₭ 230 | - `lari`: ₾ 231 | - `manat`: ₼ 232 | - `naira`: ₦ 233 | - `pataca`: $ 234 | - `riel`: ៛ 235 | - `peso.philippine`: ₱ 236 | - `rupee.indian`: ₹ 237 | - `rupee.generic`: ₨ 238 | - `rupee.tamil`: ௹ 239 | - `rupee.wancho`: 𞋿 240 | - `shekel`: ₪ 241 | - `som`: ⃀ 242 | - `taka`: ৳ 243 | - `taman`: ߿ 244 | - `tenge`: ₸ 245 | - `togrog`: ₮ 246 | - `yuan`: ¥ 247 | 248 | - Miscellaneous Technical 249 | - `smile`: ⌣ 250 | - `frown`: ⌢ 251 | - `power.standby`: ⏻ 252 | - `power.on`: ⏽ 253 | - `power.off`: ⭘ 254 | - `power.on.off`: ⏼ 255 | - `power.sleep`: ⏾ 256 | 257 | - Cyrillic 258 | - `sha`: ш 259 | - `Sha`: Ш 260 | 261 | - Greek 262 | - `digamma`: ϝ 263 | - `epsilon.alt.rev`: ϶ 264 | - `iota.inv`: ℩ 265 | - `Digamma`: Ϝ 266 | - `Theta.alt`: ϴ 267 | 268 | - Astronomical 269 | - `earth`: 🜨 270 | - `earth.alt`: ♁ 271 | - `jupiter`: ♃ 272 | - `mars`: ♂ 273 | - `mercury`: ☿ 274 | - `neptune`: ♆ 275 | - `neptune.alt`: ⯉ 276 | - `saturn`: ♄ 277 | - `sun`: ☉ 278 | - `uranus`: ⛢ 279 | - `uranus.alt`: ♅ 280 | - `venus`: ♀ 281 | 282 | - Gender 283 | - `gender.female`: ♀ 284 | - `gender.female.double`: ⚢ 285 | - `gender.female.male`: ⚤ 286 | - `gender.intersex`: ⚥ 287 | - `gender.male`: ♂ 288 | - `gender.male.double`: ⚣ 289 | - `gender.male.female`: ⚤ 290 | - `gender.male.stroke`: ⚦ 291 | - `gender.male.stroke.t`: ⚨ 292 | - `gender.male.stroke.r`: ⚩ 293 | - `gender.neuter`: ⚲ 294 | - `gender.trans`: ⚧ 295 | 296 | ### New in `emoji` 297 | 298 | - `donkey`: 🫏 299 | - `face.shaking`: 🫨 300 | - `faith.khanda`: 🪯 301 | - `flower.hyacinth`: 🪻 302 | - `flute`: 🪈 303 | - `ginger`: 🫚 304 | - `goose`: 🪿 305 | - `hairpick`: 🪮 306 | - `hand.pushing.l`: 🫷 307 | - `hand.pushing.r`: 🫸 308 | - `handfan`: 🪭 309 | - `heart.gray`: 🩶 310 | - `heart.lightblue`: 🩵 311 | - `heart.pink`: 🩷 312 | - `jellyfish`: 🪼 313 | - `maracas`: 🪇 314 | - `moose`: 🫎 315 | - `peapod`: 🫛 316 | - `wing`: 🪽 317 | - `wireless`: 🛜 318 | - `dancing.bunny.men`: 👯‍♂ 319 | - `dancing.bunny.women`: 👯‍♀ 320 | 321 | ### Deprecated 322 | 323 | - Hebrew 324 | - `alef`, use `aleph` instead 325 | - `bet`, use `beth` instead 326 | - `gimmel`, use `gimel` instead 327 | - `dalet`, use `daleth` instead 328 | - `shin`, perhaps use `sha` instead 329 | 330 | - CJK compatibility 331 | - `ast.small`, use ﹡ or `\u{fe61}` instead 332 | - `plus.small`, use ﹢ or `\u{fe62}` instead 333 | - `eq.small`, use ﹦ or `\u{fe66}` instead 334 | - `gt.small`, use ﹥ or `\u{fe65}` instead 335 | - `lt.small`, use ﹤ or `\u{fe64}` instead 336 | 337 | - `circle` -> `o` for mathematical operators 338 | - `bar.v.circle`, use `bar.v.o` instead 339 | - `ast.circle`, use `convolve.o` or `ast.op.o` instead 340 | - `backslash.circle`, use `backslash.o` instead 341 | - `dash.circle`, use `dash.o` instead 342 | - `dot.circle`, use `dot.o` instead 343 | - `dot.circle.big`, use `dot.o.big` instead 344 | - `plus.circle`, use `plus.o` instead 345 | - `plus.circle.arrow`, use `plus.o.arrow` instead 346 | - `plus.circle.big`, use `plus.o.big` instead 347 | - `minus.circle`, use `minus.o` instead 348 | - `div.circle`, use `div.o` instead 349 | - `times.circle`, use `times.o` instead 350 | - `times.circle.big`, use `times.o.big` instead 351 | - `eq.circle`, use `eq.o` instead 352 | - `gt.circle`, use `gt.o` instead 353 | - `lt.circle`, use `lt.o` instead 354 | - `parallel.circle`, use `parallel.o` instead 355 | - `perp.circle`, use `perp.o` instead 356 | - `circle.nested`, use `compose.o` instead 357 | 358 | - `angle` -> `chevron` 359 | - `angle.l` and `angle.r` to `chevron.l` and `chevron.r`, respectively 360 | - `quote.angle` to `quote.chevron` 361 | 362 | - `double` -> `stroked` for double-struck delimiters 363 | - `paren.double`, use `paren.stroked` instead 364 | - `bracket.double`, use `bracket.stroked` instead 365 | - `shell.double`, use `shell.stroked` instead 366 | 367 | - Other 368 | - `diff`, use `partial` instead 369 | - `angle.spheric.top`, use `angle.spheric.t` instead 370 | - `angle.right.sq`, use `angle.right.square` instead 371 | - `planck.reduce`, use `planck` instead 372 | - `angle.oblique`, use `angle.obtuse` instead 373 | - `kai`, use ϗ or `\u{3d7}` instead 374 | - `Kai`, use Ϗ or `\u{3c5}` instead 375 | - `franc`, because the symbol was never used in practice 376 | 377 | ## Version 0.1.1 (February 5, 2025) 378 | Brings back `angstrom`, but uses U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE, which is the one that should be used in place of the deprecated U+212B ANGSTROM SIGN. 379 | 380 | ## Version 0.1.0 (February 4, 2025) 381 | _As this is the first release of codex, the symbol changes are relative to Typst 0.12.0._ 382 | - New 383 | - `inter`, `inter.and`, `inter.big`, `inter.dot`, `inter.double`, `inter.sq`, `inter.sq.big`, `inter.sq.double`, `integral.inter` 384 | - `asymp`, `asymp.not` 385 | - `mapsto`, `mapsto.long` 386 | - `divides.not.rev`, `divides.struck` 387 | - `interleave`, `interleave.big`, `interleave.struck` 388 | - `eq.triple.not`, `eq.dots`, `eq.dots.down`, `eq.dots.up` 389 | - `smt`, `smt.eq`, `lat`, `lat.eq` 390 | - `colon.tri`, `colon.tri.op` 391 | - `dagger.triple`, `dagger.l`, `dagger.r`, `dagger.inv` 392 | - `hourglass.stroked`, `hourglass.filled` 393 | - `die.six`, `die.five`, `die.four`, `die.three`, `die.two`, `die.one` 394 | - `errorbar.square.stroked`, `errorbar.square.filled`, `errorbar.diamond.stroked`, `errorbar.diamond.filled`, `errorbar.circle.stroked`, `errorbar.circle.filled` 395 | - `numero` 396 | - `Omega.inv` 397 | - Renamed 398 | - `ohm.inv` to `Omega.inv` 399 | - Changed codepoint 400 | - `angle.l.double` from `《` to `⟪` 401 | - `angle.r.double` from `》` to `⟫` 402 | - Deprecated 403 | - `sect` and all its variants 404 | - `integral.sect` 405 | - Removed 406 | - `degree.c`, `degree.f`, `ohm`, `ohm.inv`, `angstrom`, `kelvin` 407 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Human-friendly notation for Unicode symbols. 2 | //! 3 | //! ## Model 4 | //! A [`Symbol`] is a collection of one or more _variants_. Each variant is 5 | //! identified by a set of [_modifiers_](ModifierSet) and has a string as its 6 | //! value. The modifiers themselves can in principle be any non-empty strings 7 | //! that don't contain the character `.`, but codex only defines ones that are 8 | //! entirely made of ASCII alphabetical characters. 9 | 10 | pub use self::shared::ModifierSet; 11 | 12 | mod shared; 13 | 14 | #[cfg(feature = "styling")] 15 | pub mod styling; 16 | 17 | /// A module of definitions. 18 | #[derive(Debug, Copy, Clone)] 19 | pub struct Module(&'static [(&'static str, Binding)]); 20 | 21 | impl Module { 22 | /// Try to get a bound definition in the module. 23 | pub fn get(&self, name: &str) -> Option { 24 | self.0 25 | .binary_search_by_key(&name, |(k, _)| k) 26 | .ok() 27 | .map(|i| self.0[i].1) 28 | } 29 | 30 | /// Iterate over the module's definition. 31 | pub fn iter(&self) -> impl Iterator { 32 | self.0.iter().copied() 33 | } 34 | } 35 | 36 | /// A definition bound in a module, with metadata. 37 | #[derive(Debug, Copy, Clone)] 38 | pub struct Binding { 39 | /// The bound definition. 40 | pub def: Def, 41 | /// A deprecation message for the definition, if it is deprecated. 42 | pub deprecation: Option<&'static str>, 43 | } 44 | 45 | impl Binding { 46 | /// Create a new bound definition. 47 | pub const fn new(definition: Def) -> Self { 48 | Self { def: definition, deprecation: None } 49 | } 50 | } 51 | 52 | /// A definition in a module. 53 | #[derive(Debug, Copy, Clone)] 54 | pub enum Def { 55 | /// A symbol, potentially with modifiers. 56 | Symbol(Symbol), 57 | /// A nested module. 58 | Module(Module), 59 | } 60 | 61 | /// A symbol, either a leaf or with modifiers and optional deprecation. 62 | #[derive(Debug, Copy, Clone)] 63 | pub enum Symbol { 64 | /// A symbol without modifiers. 65 | Single(&'static str), 66 | /// A symbol with named modifiers. The symbol defaults to its first variant. 67 | Multi(&'static [(ModifierSet<&'static str>, &'static str, Option<&'static str>)]), 68 | } 69 | 70 | impl Symbol { 71 | /// Get the symbol's variant for a given set of modifiers, alongside an optional deprecation 72 | /// message. 73 | pub fn get(&self, modifs: ModifierSet<&str>) -> Option<(&'static str, Option<&str>)> { 74 | match self { 75 | Self::Single(c) => modifs.is_empty().then_some((*c, None)), 76 | Self::Multi(list) => { 77 | modifs.best_match_in(list.iter().copied().map(|(m, c, d)| (m, (c, d)))) 78 | } 79 | } 80 | } 81 | 82 | /// Iterate over the variants of this symbol. 83 | /// 84 | /// Each variant is represented by a tuple `(modifiers, value, deprecation)`. 85 | pub fn variants( 86 | &self, 87 | ) -> impl Iterator, &'static str, Option<&'static str>)> 88 | { 89 | enum Variants { 90 | Single(std::iter::Once<&'static str>), 91 | Multi( 92 | std::slice::Iter< 93 | 'static, 94 | (ModifierSet<&'static str>, &'static str, Option<&'static str>), 95 | >, 96 | ), 97 | } 98 | let mut iter = match self { 99 | Self::Single(c) => Variants::Single(std::iter::once(*c)), 100 | Self::Multi(sl) => Variants::Multi(sl.iter()), 101 | }; 102 | std::iter::from_fn(move || match &mut iter { 103 | Variants::Single(iter) => Some((ModifierSet::default(), iter.next()?, None)), 104 | Variants::Multi(iter) => iter.next().copied(), 105 | }) 106 | } 107 | 108 | /// Possible modifiers for this symbol. 109 | pub fn modifiers(&self) -> impl Iterator + '_ { 110 | self.variants() 111 | .flat_map(|(m, _, _)| m.into_iter()) 112 | .collect::>() 113 | .into_iter() 114 | } 115 | } 116 | 117 | /// A module that contains the other top-level modules. 118 | pub const ROOT: Module = Module(&[ 119 | ("emoji", Binding::new(Def::Module(EMOJI))), 120 | ("sym", Binding::new(Def::Module(SYM))), 121 | ]); 122 | 123 | include!(concat!(env!("OUT_DIR"), "/out.rs")); 124 | 125 | #[cfg(test)] 126 | mod test { 127 | use super::*; 128 | use std::collections::BTreeSet; 129 | #[cfg(feature = "_test-unicode-conformance")] 130 | use std::collections::HashSet; 131 | 132 | #[test] 133 | fn all_modules_sorted() { 134 | fn assert_sorted_recursively(root: Module) { 135 | assert!(root.0.is_sorted_by_key(|(k, _)| k)); 136 | 137 | for (_, entry) in root.iter() { 138 | if let Def::Module(module) = entry.def { 139 | assert_sorted_recursively(module) 140 | } 141 | } 142 | } 143 | 144 | assert_sorted_recursively(ROOT); 145 | } 146 | 147 | #[test] 148 | fn unicode_escapes() { 149 | let Def::Symbol(wj) = SYM.get("wj").unwrap().def else { panic!() }; 150 | assert_eq!(wj.get(ModifierSet::default()).unwrap().0, "\u{2060}"); 151 | let Def::Symbol(space) = SYM.get("space").unwrap().def else { panic!() }; 152 | assert_eq!(space.get(ModifierSet::default()).unwrap().0, " "); 153 | assert_eq!( 154 | space.get(ModifierSet::from_raw_dotted("nobreak")).unwrap().0, 155 | "\u{A0}" 156 | ); 157 | } 158 | 159 | #[test] 160 | fn random_sample() { 161 | for (key, control) in [ 162 | ( 163 | "backslash", 164 | [("", "\\"), ("not", "⧷"), ("o", "⦸")].as_slice(), 165 | ), 166 | ("chi", &[("", "χ")]), 167 | ("forces", &[("", "⊩"), ("not", "⊮")]), 168 | ("interleave", &[("", "⫴"), ("big", "⫼"), ("struck", "⫵")]), 169 | ("uranus", &[("", "⛢"), ("alt", "♅")]), 170 | ] { 171 | let Def::Symbol(s) = SYM.get(key).unwrap().def else { 172 | panic!("{key:?} is not a symbol") 173 | }; 174 | let variants = s 175 | .variants() 176 | .map(|(m, v, _)| (m.into_iter().collect::>(), v)) 177 | .collect::>(); 178 | let control = control 179 | .iter() 180 | .map(|&(m, v)| { 181 | ( 182 | ModifierSet::from_raw_dotted(m) 183 | .into_iter() 184 | .collect::>(), 185 | v, 186 | ) 187 | }) 188 | .collect::>(); 189 | 190 | assert_eq!(variants, control); 191 | } 192 | } 193 | 194 | /// https://www.unicode.org/reports/tr51/#def_text_presentation_selector. 195 | const TEXT_PRESENTATION_SELECTOR: char = '\u{FE0E}'; 196 | /// https://www.unicode.org/reports/tr51/#def_emoji_presentation_selector. 197 | const EMOJI_PRESENTATION_SELECTOR: char = '\u{FE0F}'; 198 | 199 | #[test] 200 | fn symbols_are_not_emojis() { 201 | assert!( 202 | are_all_variants_valid( 203 | SYM, 204 | |c| !c.contains(EMOJI_PRESENTATION_SELECTOR), 205 | ) , 206 | "unexpected use of emoji presentation selector in `sym` (see list above)", 207 | ) 208 | } 209 | 210 | #[test] 211 | fn emojis_are_not_text() { 212 | assert!( 213 | are_all_variants_valid( 214 | EMOJI, 215 | |c| !c.contains(TEXT_PRESENTATION_SELECTOR), 216 | ) , 217 | "unexpected use of text presentation selector in `emoji` (see list above)", 218 | ) 219 | } 220 | 221 | /// Returns the list of presentation sequences defined by Unicode. 222 | /// 223 | /// See: https://www.unicode.org/reports/tr51/#Emoji_Variation_Sequences. 224 | #[cfg(feature = "_test-unicode-conformance")] 225 | fn get_valid_presentation_sequences() -> HashSet { 226 | include_str!(concat!(env!("OUT_DIR"), "/emoji-variation-sequences.txt")) 227 | .lines() 228 | .filter_map(|l| { 229 | let line = l.split('#').next().unwrap_or(l); 230 | (!line.is_empty()).then_some(line) 231 | }) 232 | .map(|line| { 233 | line.split(';') 234 | .next() 235 | .unwrap() 236 | .split_whitespace() 237 | .map(|cp| { 238 | char::from_u32(u32::from_str_radix(cp, 0x10).unwrap()).unwrap() 239 | }) 240 | .collect() 241 | }) 242 | .collect() 243 | } 244 | 245 | #[cfg(feature = "_test-unicode-conformance")] 246 | #[test] 247 | fn no_invalid_presentation_sequence() { 248 | let sequences = get_valid_presentation_sequences(); 249 | assert!( 250 | are_all_variants_valid(ROOT, |c| { 251 | if c.contains(TEXT_PRESENTATION_SELECTOR) 252 | || c.contains(EMOJI_PRESENTATION_SELECTOR) 253 | { 254 | sequences.contains(c) 255 | } else { 256 | true 257 | } 258 | }), 259 | "invalid presentation sequence(s) (see list above)", 260 | ) 261 | } 262 | 263 | #[cfg(feature = "_test-unicode-conformance")] 264 | #[test] 265 | fn symbols_have_text_presentation() { 266 | let require_presentation_selector = get_valid_presentation_sequences() 267 | .into_iter() 268 | .map(|s| s.chars().next().unwrap()) 269 | .collect::>(); 270 | assert!( 271 | are_all_variants_valid(SYM, |c| { 272 | // All emoji variation sequences are exactly 2 codepoints long 273 | // as of Unicode 16.0, so this doesn't miss anything. 274 | !(c.chars().count() == 1 275 | && require_presentation_selector.contains(&c.chars().next().unwrap())) 276 | }), 277 | "missing text presentation selector(s) in `sym` (see list above)", 278 | ) 279 | } 280 | 281 | #[cfg(feature = "_test-unicode-conformance")] 282 | #[test] 283 | fn emojis_have_emoji_presentation() { 284 | let require_presentation_selector = get_valid_presentation_sequences() 285 | .into_iter() 286 | .map(|s| s.chars().next().unwrap()) 287 | .collect::>(); 288 | assert!( 289 | are_all_variants_valid(EMOJI, |c| { 290 | // All emoji variation sequences are exactly 2 codepoints long 291 | // as of Unicode 16.0, so this doesn't miss anything. 292 | !(c.chars().count() == 1 293 | && require_presentation_selector.contains(&c.chars().next().unwrap())) 294 | }), 295 | "missing emoji presentation selector(s) in `emoji` (see list above)", 296 | ) 297 | } 298 | 299 | /// Returns `false` if, and only if, the predicate returned `false` for at least one variant 300 | /// within the module. 301 | /// 302 | /// Prints all variants for which the predicate returns `false`. 303 | fn are_all_variants_valid bool>( 304 | module: Module, 305 | mut predicate: P, 306 | ) -> bool { 307 | let mut all_valid = true; 308 | fn aux bool>( 309 | module: Module, 310 | path: Vec<&'static str>, 311 | all_valid: &mut bool, 312 | predicate: &mut P, 313 | ) { 314 | for (name, binding) in module.iter() { 315 | let mut new_path = path.clone(); 316 | new_path.push(name); 317 | match binding.def { 318 | Def::Symbol(s) => { 319 | for (modifiers, c, _) in s.variants() { 320 | if !predicate(c) { 321 | *all_valid = false; 322 | eprintln!( 323 | "- {}{}{} {} ({})", 324 | new_path.join("."), 325 | if modifiers.is_empty() { "" } else { "." }, 326 | modifiers.as_str(), 327 | c, 328 | c.chars() 329 | .map(|cp| format!("{:04X}", cp as u32)) 330 | .collect::>() 331 | .join(" "), 332 | ) 333 | } 334 | } 335 | } 336 | Def::Module(m) => { 337 | aux(m, new_path, all_valid, predicate); 338 | } 339 | } 340 | } 341 | } 342 | aux(module, Vec::new(), &mut all_valid, &mut predicate); 343 | all_valid 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /src/modules/sym.txt: -------------------------------------------------------------------------------- 1 | // Control. 2 | wj \u{2060} 3 | zwj \u{200D} 4 | zwnj \u{200C} 5 | zws \u{200B} 6 | lrm \u{200E} 7 | rlm \u{200F} 8 | 9 | // Spaces. 10 | space \u{20} 11 | .nobreak \u{A0} 12 | .nobreak.narrow \u{202F} 13 | .en \u{2002} 14 | .quad \u{2003} 15 | .third \u{2004} 16 | .quarter \u{2005} 17 | .sixth \u{2006} 18 | .med \u{205F} 19 | .fig \u{2007} 20 | .punct \u{2008} 21 | .thin \u{2009} 22 | .hair \u{200A} 23 | 24 | // Delimiters. 25 | paren 26 | .l ( 27 | .l.flat ⟮ 28 | .l.closed ⦇ 29 | .l.stroked ⦅ 30 | .r ) 31 | .r.flat ⟯ 32 | .r.closed ⦈ 33 | .r.stroked ⦆ 34 | .t ⏜ 35 | .b ⏝ 36 | brace 37 | .l \u{7B} 38 | .l.stroked ⦃ 39 | .r \u{7D} 40 | .r.stroked ⦄ 41 | .t ⏞ 42 | .b ⏟ 43 | bracket 44 | .l [ 45 | .l.tick.t ⦍ 46 | .l.tick.b ⦏ 47 | .l.stroked ⟦ 48 | .r ] 49 | .r.tick.t ⦐ 50 | .r.tick.b ⦎ 51 | .r.stroked ⟧ 52 | .t ⎴ 53 | .b ⎵ 54 | shell 55 | .l ❲ 56 | .l.stroked ⟬ 57 | .l.filled ⦗ 58 | .r ❳ 59 | .r.stroked ⟭ 60 | .r.filled ⦘ 61 | .t ⏠ 62 | .b ⏡ 63 | bag 64 | .l ⟅ 65 | .r ⟆ 66 | mustache 67 | .l ⎰ 68 | .r ⎱ 69 | bar 70 | .v | 71 | .v.double ‖ 72 | .v.triple ⦀ 73 | .v.broken ¦ 74 | .v.o ⦶ 75 | .h ― 76 | fence 77 | .l ⧘ 78 | .l.double ⧚ 79 | .r ⧙ 80 | .r.double ⧛ 81 | .dotted ⦙ 82 | chevron 83 | .l ⟨ 84 | .l.curly ⧼ 85 | .l.dot ⦑ 86 | .l.closed ⦉ 87 | .l.double ⟪ 88 | .r ⟩ 89 | .r.curly ⧽ 90 | .r.dot ⦒ 91 | .r.closed ⦊ 92 | .r.double ⟫ 93 | ceil 94 | .l ⌈ 95 | .r ⌉ 96 | floor 97 | .l ⌊ 98 | .r ⌋ 99 | corner 100 | .l.t ⌜ 101 | .l.b ⌞ 102 | .r.t ⌝ 103 | .r.b ⌟ 104 | 105 | // Punctuation. 106 | amp & 107 | .inv ⅋ 108 | ast 109 | .op ∗ 110 | .op.o ⊛ 111 | .basic *\vs{text} 112 | .low ⁎ 113 | .double ⁑ 114 | .triple ⁂ 115 | .square ⧆ 116 | at @ 117 | backslash \u{005C} 118 | .o ⦸ 119 | .not ⧷ 120 | co ℅ 121 | colon : 122 | .currency ₡ 123 | .double ∷ 124 | .tri ⁝ 125 | .tri.op ⫶ 126 | .eq ≔ 127 | .double.eq ⩴ 128 | comma , 129 | .inv ⸲ 130 | .rev ⹁ 131 | dagger † 132 | .double ‡ 133 | .triple ⹋ 134 | .l ⸶ 135 | .r ⸷ 136 | .inv ⸸ 137 | dash 138 | .en – 139 | .em — 140 | .em.two ⸺ 141 | .em.three ⸻ 142 | .fig ‒ 143 | .colon ∹ 144 | .o ⊝ 145 | .wave 〜 146 | .wave.double 〰\vs{text} 147 | dot 148 | .op ⋅ 149 | .basic \u{2E} 150 | .c · 151 | .o ⊙ 152 | .o.big ⨀ 153 | .square ⊡ 154 | .double ¨ 155 | .triple \u{20DB} 156 | .quad \u{20DC} 157 | excl ! 158 | .double ‼\vs{text} 159 | .inv ¡ 160 | .quest ⁉\vs{text} 161 | quest ? 162 | .double ⁇ 163 | .excl ⁈ 164 | .inv ¿ 165 | interrobang ‽ 166 | .inv ⸘ 167 | hash #\vs{text} 168 | hyph ‐ 169 | .minus \u{2D} 170 | .nobreak \u{2011} 171 | .point ‧ 172 | .soft \u{AD} 173 | numero № 174 | percent % 175 | permille ‰ 176 | permyriad ‱ 177 | pilcrow ¶ 178 | .rev ⁋ 179 | section § 180 | semi ; 181 | .inv ⸵ 182 | .rev ⁏ 183 | slash / 184 | .o ⊘ 185 | .double ⫽ 186 | .triple ⫻ 187 | .big ⧸ 188 | dots 189 | .h.c ⋯ 190 | .h … 191 | .v ⋮ 192 | .down ⋱ 193 | .up ⋰ 194 | tilde 195 | .op ∼ 196 | .basic ~ 197 | .dot ⩪ 198 | .eq ≃ 199 | .eq.not ≄ 200 | .eq.rev ⋍ 201 | .equiv ≅ 202 | .equiv.not ≇ 203 | .nequiv ≆ 204 | .not ≁ 205 | .rev ∽ 206 | .rev.equiv ≌ 207 | .triple ≋ 208 | 209 | // Accents, quotes, and primes. 210 | acute ´ 211 | .double ˝ 212 | breve ˘ 213 | caret ‸ 214 | caron ˇ 215 | hat ^ 216 | diaer ¨ 217 | grave ` 218 | macron ¯ 219 | quote 220 | .double " 221 | .single ' 222 | .l.double “ 223 | .l.single ‘ 224 | .r.double ” 225 | .r.single ’ 226 | .chevron.l.double « 227 | .chevron.l.single ‹ 228 | .chevron.r.double » 229 | .chevron.r.single › 230 | .high.double ‟ 231 | .high.single ‛ 232 | .low.double „ 233 | .low.single ‚ 234 | prime ′ 235 | .rev ‵ 236 | .double ″ 237 | .double.rev ‶ 238 | .triple ‴ 239 | .triple.rev ‷ 240 | .quad ⁗ 241 | 242 | // https://en.wikipedia.org/wiki/List_of_mathematical_symbols_by_subject 243 | 244 | // Arithmetic. 245 | plus + 246 | .o ⊕ 247 | .o.l ⨭ 248 | .o.r ⨮ 249 | .o.arrow ⟴ 250 | .o.big ⨁ 251 | .dot ∔ 252 | .double ⧺ 253 | .minus ± 254 | .square ⊞ 255 | .triangle ⨹ 256 | .triple ⧻ 257 | minus − 258 | .o ⊖ 259 | .dot ∸ 260 | .plus ∓ 261 | .square ⊟ 262 | .tilde ≂ 263 | .triangle ⨺ 264 | div ÷ 265 | .o ⨸ 266 | .slanted.o ⦼ 267 | times × 268 | .big ⨉ 269 | .o ⊗ 270 | .o.l ⨴ 271 | .o.r ⨵ 272 | .o.hat ⨶ 273 | .o.big ⨂ 274 | .div ⋇ 275 | .three.l ⋋ 276 | .three.r ⋌ 277 | .l ⋉ 278 | .r ⋊ 279 | .square ⊠ 280 | .triangle ⨻ 281 | ratio ∶ 282 | 283 | // Relations. 284 | eq = 285 | .star ≛ 286 | .o ⊜ 287 | .colon ≕ 288 | .dots ≑ 289 | .dots.down ≒ 290 | .dots.up ≓ 291 | .def ≝ 292 | .delta ≜ 293 | .equi ≚ 294 | .est ≙ 295 | .gt ⋝ 296 | .lt ⋜ 297 | .m ≞ 298 | .not ≠ 299 | .prec ⋞ 300 | .quest ≟ 301 | .succ ⋟ 302 | .triple ≡ 303 | .triple.not ≢ 304 | .quad ≣ 305 | gt > 306 | .o ⧁ 307 | .dot ⋗ 308 | .approx ⪆ 309 | .double ≫ 310 | .eq ≥ 311 | .eq.slant ⩾ 312 | .eq.lt ⋛ 313 | .eq.not ≱ 314 | .equiv ≧ 315 | .lt ≷ 316 | .lt.not ≹ 317 | .neq ⪈ 318 | .napprox ⪊ 319 | .nequiv ≩ 320 | .not ≯ 321 | .ntilde ⋧ 322 | .tilde ≳ 323 | .tilde.not ≵ 324 | .tri ⊳ 325 | .tri.eq ⊵ 326 | .tri.eq.not ⋭ 327 | .tri.not ⋫ 328 | .triple ⋙ 329 | .triple.nested ⫸ 330 | lt < 331 | .o ⧀ 332 | .dot ⋖ 333 | .approx ⪅ 334 | .double ≪ 335 | .eq ≤ 336 | .eq.slant ⩽ 337 | .eq.gt ⋚ 338 | .eq.not ≰ 339 | .equiv ≦ 340 | .gt ≶ 341 | .gt.not ≸ 342 | .neq ⪇ 343 | .napprox ⪉ 344 | .nequiv ≨ 345 | .not ≮ 346 | .ntilde ⋦ 347 | .tilde ≲ 348 | .tilde.not ≴ 349 | .tri ⊲ 350 | .tri.eq ⊴ 351 | .tri.eq.not ⋬ 352 | .tri.not ⋪ 353 | .triple ⋘ 354 | .triple.nested ⫷ 355 | approx ≈ 356 | .eq ≊ 357 | .not ≉ 358 | prec ≺ 359 | .approx ⪷ 360 | .curly.eq ≼ 361 | .curly.eq.not ⋠ 362 | .double ⪻ 363 | .eq ⪯ 364 | .equiv ⪳ 365 | .napprox ⪹ 366 | .neq ⪱ 367 | .nequiv ⪵ 368 | .not ⊀ 369 | .ntilde ⋨ 370 | .tilde ≾ 371 | succ ≻ 372 | .approx ⪸ 373 | .curly.eq ≽ 374 | .curly.eq.not ⋡ 375 | .double ⪼ 376 | .eq ⪰ 377 | .equiv ⪴ 378 | .napprox ⪺ 379 | .neq ⪲ 380 | .nequiv ⪶ 381 | .not ⊁ 382 | .ntilde ⋩ 383 | .tilde ≿ 384 | equiv ≡ 385 | .not ≢ 386 | smt ⪪ 387 | .eq ⪬ 388 | lat ⪫ 389 | .eq ⪭ 390 | prop ∝ 391 | original ⊶ 392 | image ⊷ 393 | asymp ≍ 394 | .not ≭ 395 | 396 | // Set theory. 397 | emptyset ∅ 398 | .arrow.r ⦳ 399 | .arrow.l ⦴ 400 | .bar ⦱ 401 | .circle ⦲ 402 | .rev ⦰ 403 | nothing ∅ 404 | .arrow.r ⦳ 405 | .arrow.l ⦴ 406 | .bar ⦱ 407 | .circle ⦲ 408 | .rev ⦰ 409 | without ∖ 410 | complement ∁ 411 | in ∈ 412 | .not ∉ 413 | .rev ∋ 414 | .rev.not ∌ 415 | .rev.small ∍ 416 | .small ∊ 417 | subset ⊂ 418 | .approx ⫉ 419 | .closed ⫏ 420 | .closed.eq ⫑ 421 | .dot ⪽ 422 | .double ⋐ 423 | .eq ⊆ 424 | .eq.dot ⫃ 425 | .eq.not ⊈ 426 | .eq.sq ⊑ 427 | .eq.sq.not ⋢ 428 | .equiv ⫅ 429 | .neq ⊊ 430 | .nequiv ⫋ 431 | .not ⊄ 432 | .plus ⪿ 433 | .sq ⊏ 434 | .sq.neq ⋤ 435 | .tilde ⫇ 436 | .times ⫁ 437 | supset ⊃ 438 | .approx ⫊ 439 | .closed ⫐ 440 | .closed.eq ⫒ 441 | .dot ⪾ 442 | .double ⋑ 443 | .eq ⊇ 444 | .eq.dot ⫄ 445 | .eq.not ⊉ 446 | .eq.sq ⊒ 447 | .eq.sq.not ⋣ 448 | .equiv ⫆ 449 | .neq ⊋ 450 | .nequiv ⫌ 451 | .not ⊅ 452 | .plus ⫀ 453 | .sq ⊐ 454 | .sq.neq ⋥ 455 | .tilde ⫈ 456 | .times ⫂ 457 | union ∪ 458 | .arrow ⊌ 459 | .big ⋃ 460 | .dot ⊍ 461 | .dot.big ⨃ 462 | .double ⋓ 463 | .minus ⩁ 464 | .or ⩅ 465 | .plus ⊎ 466 | .plus.big ⨄ 467 | .sq ⊔ 468 | .sq.big ⨆ 469 | .sq.double ⩏ 470 | inter ∩ 471 | .and ⩄ 472 | .big ⋂ 473 | .dot ⩀ 474 | .double ⋒ 475 | .sq ⊓ 476 | .sq.big ⨅ 477 | .sq.double ⩎ 478 | 479 | // Calculus. 480 | infinity ∞ 481 | .bar ⧞ 482 | .incomplete ⧜ 483 | .tie ⧝ 484 | oo ∞ 485 | partial ∂ 486 | gradient ∇ 487 | nabla ∇ 488 | sum ∑ 489 | .integral ⨋ 490 | product ∏ 491 | .co ∐ 492 | integral ∫ 493 | .arrow.hook ⨗ 494 | .ccw ⨑ 495 | .cont ∮ 496 | .cont.ccw ∳ 497 | .cont.cw ∲ 498 | .cw ∱ 499 | .dash ⨍ 500 | .dash.double ⨎ 501 | .double ∬ 502 | .quad ⨌ 503 | .inter ⨙ 504 | .slash ⨏ 505 | .square ⨖ 506 | .surf ∯ 507 | .times ⨘ 508 | .triple ∭ 509 | .union ⨚ 510 | .vol ∰ 511 | laplace ∆ 512 | 513 | // Logic. 514 | forall ∀ 515 | exists ∃ 516 | .not ∄ 517 | top ⊤ 518 | bot ⊥ 519 | not ¬ 520 | and ∧ 521 | .big ⋀ 522 | .curly ⋏ 523 | .dot ⟑ 524 | .double ⩓ 525 | or ∨ 526 | .big ⋁ 527 | .curly ⋎ 528 | .dot ⟇ 529 | .double ⩔ 530 | xor ⊕ 531 | .big ⨁ 532 | models ⊧ 533 | forces ⊩ 534 | .not ⊮ 535 | therefore ∴ 536 | because ∵ 537 | qed ∎ 538 | 539 | // Function and category theory. 540 | mapsto ↦ 541 | .long ⟼ 542 | compose ∘ 543 | .o ⊚ 544 | convolve ∗ 545 | .o ⊛ 546 | multimap ⊸ 547 | .double ⧟ 548 | 549 | // Game theory. 550 | tiny ⧾ 551 | miny ⧿ 552 | 553 | // Number theory. 554 | divides ∣ 555 | .not ∤ 556 | .not.rev ⫮ 557 | .struck ⟊ 558 | 559 | // Algebra. 560 | wreath ≀ 561 | 562 | // Geometry. 563 | angle ∠ 564 | .acute ⦟ 565 | .arc ∡ 566 | .arc.rev ⦛ 567 | .azimuth ⍼ 568 | .obtuse ⦦ 569 | .rev ⦣ 570 | .right ∟ 571 | .right.rev ⯾ 572 | .right.arc ⊾ 573 | .right.dot ⦝ 574 | .right.square ⦜ 575 | .s ⦞ 576 | .spatial ⟀ 577 | .spheric ∢ 578 | .spheric.rev ⦠ 579 | .spheric.t ⦡ 580 | angzarr ⍼ 581 | parallel ∥ 582 | .struck ⫲ 583 | .o ⦷ 584 | .eq ⋕ 585 | .equiv ⩨ 586 | .not ∦ 587 | .slanted.eq ⧣ 588 | .slanted.eq.tilde ⧤ 589 | .slanted.equiv ⧥ 590 | .tilde ⫳ 591 | perp ⟂ 592 | .o ⦹ 593 | 594 | // Astronomical. 595 | earth 🜨 596 | .alt ♁ 597 | jupiter ♃ 598 | mars ♂\vs{text} 599 | mercury ☿ 600 | neptune ♆ 601 | .alt ⯉ 602 | saturn ♄ 603 | sun ☉ 604 | uranus ⛢ 605 | .alt ♅ 606 | venus ♀\vs{text} 607 | 608 | // Miscellaneous Technical. 609 | diameter ⌀ 610 | interleave ⫴ 611 | .big ⫼ 612 | .struck ⫵ 613 | join ⨝ 614 | .r ⟖ 615 | .l ⟕ 616 | .l.r ⟗ 617 | hourglass 618 | .stroked ⧖ 619 | .filled ⧗ 620 | degree ° 621 | smash ⨳ 622 | power 623 | .standby ⏻ 624 | .on ⏽ 625 | .off ⭘ 626 | .on.off ⏼ 627 | .sleep ⏾ 628 | smile ⌣ 629 | frown ⌢ 630 | 631 | // Currency. 632 | afghani ؋ 633 | baht ฿ 634 | bitcoin ₿ 635 | cedi ₵ 636 | cent ¢ 637 | currency ¤ 638 | dollar $ 639 | dong ₫ 640 | dorome ߾ 641 | dram ֏ 642 | euro € 643 | guarani ₲ 644 | hryvnia ₴ 645 | kip ₭ 646 | lari ₾ 647 | lira ₺ 648 | manat ₼ 649 | naira ₦ 650 | pataca $ 651 | peso $ 652 | .philippine ₱ 653 | pound £ 654 | riel ៛ 655 | riyal ⃁ 656 | ruble ₽ 657 | rupee 658 | .indian ₹ 659 | .generic ₨ 660 | .tamil ௹ 661 | .wancho 𞋿 662 | shekel ₪ 663 | som ⃀ 664 | taka ৳ 665 | taman ߿ 666 | tenge ₸ 667 | togrog ₮ 668 | won ₩ 669 | yen ¥ 670 | yuan ¥ 671 | 672 | // Miscellaneous. 673 | ballot ☐ 674 | .cross ☒ 675 | .check ☑\vs{text} 676 | .check.heavy 🗹 677 | checkmark ✓ 678 | .light 🗸 679 | .heavy ✔\vs{text} 680 | crossmark ✗ 681 | .heavy ✘ 682 | floral ❦ 683 | .l ☙ 684 | .r ❧ 685 | refmark ※ 686 | cc 🅭 687 | .by 🅯 688 | .nc 🄏 689 | .nd ⊜ 690 | .public 🅮 691 | .sa 🄎 692 | .zero 🄍 693 | copyright ©\vs{text} 694 | .sound ℗ 695 | copyleft 🄯 696 | trademark ™\vs{text} 697 | .registered ®\vs{text} 698 | .service ℠ 699 | maltese ✠ 700 | suit 701 | .club.filled ♣\vs{text} 702 | .club.stroked ♧ 703 | .diamond.filled ♦\vs{text} 704 | .diamond.stroked ♢ 705 | .heart.filled ♥\vs{text} 706 | .heart.stroked ♡ 707 | .spade.filled ♠\vs{text} 708 | .spade.stroked ♤ 709 | 710 | // Music. 711 | note 712 | .up 🎜 713 | .down 🎝 714 | .whole 𝅝 715 | .half 𝅗𝅥 716 | .quarter 𝅘𝅥 717 | .quarter.alt ♩ 718 | .eighth 𝅘𝅥𝅮 719 | .eighth.alt ♪ 720 | .eighth.beamed ♫ 721 | .sixteenth 𝅘𝅥𝅯 722 | .sixteenth.beamed ♬ 723 | .grace 𝆕 724 | .grace.slash 𝆔 725 | rest 726 | .whole 𝄻 727 | .multiple 𝄺 728 | .multiple.measure 𝄩 729 | .half 𝄼 730 | .quarter 𝄽 731 | .eighth 𝄾 732 | .sixteenth 𝄿 733 | natural ♮ 734 | .t 𝄮 735 | .b 𝄯 736 | flat ♭ 737 | .t 𝄬 738 | .b 𝄭 739 | .double 𝄫 740 | .quarter 𝄳 741 | sharp ♯ 742 | .t 𝄰 743 | .b 𝄱 744 | .double 𝄪 745 | .quarter 𝄲 746 | 747 | // Shapes. 748 | bullet • 749 | .op ∙ 750 | .o ⦿ 751 | .stroked ◦ 752 | .stroked.o ⦾ 753 | .hole ◘ 754 | .hyph ⁃ 755 | .tri ‣ 756 | .l ⁌ 757 | .r ⁍ 758 | circle 759 | .stroked ○ 760 | .stroked.tiny ∘ 761 | .stroked.small ⚬ 762 | .stroked.big ◯ 763 | .filled ● 764 | .filled.tiny ⦁ 765 | .filled.small ∙ 766 | .filled.big ⬤ 767 | .dotted ◌ 768 | ellipse 769 | .stroked.h ⬭ 770 | .stroked.v ⬯ 771 | .filled.h ⬬ 772 | .filled.v ⬮ 773 | triangle 774 | .stroked.t △ 775 | .stroked.b ▽ 776 | .stroked.r ▷ 777 | .stroked.l ◁ 778 | .stroked.bl ◺ 779 | .stroked.br ◿ 780 | .stroked.tl ◸ 781 | .stroked.tr ◹ 782 | .stroked.small.t ▵ 783 | .stroked.small.b ▿ 784 | .stroked.small.r ▹ 785 | .stroked.small.l ◃ 786 | .stroked.rounded 🛆 787 | .stroked.nested ⟁ 788 | .stroked.dot ◬ 789 | .filled.t ▲ 790 | .filled.b ▼ 791 | .filled.r ▶\vs{text} 792 | .filled.l ◀\vs{text} 793 | .filled.bl ◣ 794 | .filled.br ◢ 795 | .filled.tl ◤ 796 | .filled.tr ◥ 797 | .filled.small.t ▴ 798 | .filled.small.b ▾ 799 | .filled.small.r ▸ 800 | .filled.small.l ◂ 801 | square 802 | .stroked □ 803 | .stroked.tiny ▫\vs{text} 804 | .stroked.small ◽\vs{text} 805 | .stroked.medium ◻\vs{text} 806 | .stroked.big ⬜\vs{text} 807 | .stroked.dotted ⬚ 808 | .stroked.rounded ▢ 809 | .filled ■ 810 | .filled.tiny ▪\vs{text} 811 | .filled.small ◾\vs{text} 812 | .filled.medium ◼\vs{text} 813 | .filled.big ⬛\vs{text} 814 | rect 815 | .stroked.h ▭ 816 | .stroked.v ▯ 817 | .filled.h ▬ 818 | .filled.v ▮ 819 | penta 820 | .stroked ⬠ 821 | .filled ⬟ 822 | hexa 823 | .stroked ⬡ 824 | .filled ⬢ 825 | diamond 826 | .stroked ◇ 827 | .stroked.small ⋄ 828 | .stroked.medium ⬦ 829 | .stroked.dot ⟐ 830 | .filled ◆ 831 | .filled.medium ⬥ 832 | .filled.small ⬩ 833 | lozenge 834 | .stroked ◊ 835 | .stroked.small ⬫ 836 | .stroked.medium ⬨ 837 | .filled ⧫ 838 | .filled.small ⬪ 839 | .filled.medium ⬧ 840 | parallelogram 841 | .stroked ▱ 842 | .filled ▰ 843 | star 844 | .op ⋆ 845 | .stroked ☆ 846 | .filled ★ 847 | 848 | // Arrows, harpoons, and tacks. 849 | arrow 850 | .r → 851 | .r.long.bar ⟼ 852 | .r.bar ↦ 853 | .r.curve ⤷ 854 | .r.turn ⮎ 855 | .r.dashed ⇢ 856 | .r.dotted ⤑ 857 | .r.double ⇒ 858 | .r.double.bar ⤇ 859 | .r.double.long ⟹ 860 | .r.double.long.bar ⟾ 861 | .r.double.not ⇏ 862 | .r.double.struck ⤃ 863 | .r.filled ➡\vs{text} 864 | .r.hook ↪\vs{text} 865 | .r.long ⟶ 866 | .r.long.squiggly ⟿ 867 | .r.loop ↬ 868 | .r.not ↛ 869 | .r.quad ⭆ 870 | .r.squiggly ⇝ 871 | .r.stop ⇥ 872 | .r.stroked ⇨ 873 | .r.struck ⇸ 874 | .r.dstruck ⇻ 875 | .r.tail ↣ 876 | .r.tail.struck ⤔ 877 | .r.tail.dstruck ⤕ 878 | .r.tilde ⥲ 879 | .r.triple ⇛ 880 | .r.twohead ↠ 881 | .r.twohead.bar ⤅ 882 | .r.twohead.struck ⤀ 883 | .r.twohead.dstruck ⤁ 884 | .r.twohead.tail ⤖ 885 | .r.twohead.tail.struck ⤗ 886 | .r.twohead.tail.dstruck ⤘ 887 | .r.open ⇾ 888 | .r.wave ↝ 889 | .l ← 890 | .l.bar ↤ 891 | .l.curve ⤶ 892 | .l.turn ⮌ 893 | .l.dashed ⇠ 894 | .l.dotted ⬸ 895 | .l.double ⇐ 896 | .l.double.bar ⤆ 897 | .l.double.long ⟸ 898 | .l.double.long.bar ⟽ 899 | .l.double.not ⇍ 900 | .l.double.struck ⤂ 901 | .l.filled ⬅\vs{text} 902 | .l.hook ↩\vs{text} 903 | .l.long ⟵ 904 | .l.long.bar ⟻ 905 | .l.long.squiggly ⬳ 906 | .l.loop ↫ 907 | .l.not ↚ 908 | .l.quad ⭅ 909 | .l.squiggly ⇜ 910 | .l.stop ⇤ 911 | .l.stroked ⇦ 912 | .l.struck ⇷ 913 | .l.dstruck ⇺ 914 | .l.tail ↢ 915 | .l.tail.struck ⬹ 916 | .l.tail.dstruck ⬺ 917 | .l.tilde ⭉ 918 | .l.triple ⇚ 919 | .l.twohead ↞ 920 | .l.twohead.bar ⬶ 921 | .l.twohead.struck ⬴ 922 | .l.twohead.dstruck ⬵ 923 | .l.twohead.tail ⬻ 924 | .l.twohead.tail.struck ⬼ 925 | .l.twohead.tail.dstruck ⬽ 926 | .l.open ⇽ 927 | .l.wave ↜ 928 | .t ↑ 929 | .t.bar ↥ 930 | .t.curve ⤴\vs{text} 931 | .t.turn ⮍ 932 | .t.dashed ⇡ 933 | .t.double ⇑ 934 | .t.filled ⬆\vs{text} 935 | .t.quad ⟰ 936 | .t.stop ⤒ 937 | .t.stroked ⇧ 938 | .t.struck ⤉ 939 | .t.dstruck ⇞ 940 | .t.triple ⤊ 941 | .t.twohead ↟ 942 | .b ↓ 943 | .b.bar ↧ 944 | .b.curve ⤵\vs{text} 945 | .b.turn ⮏ 946 | .b.dashed ⇣ 947 | .b.double ⇓ 948 | .b.filled ⬇\vs{text} 949 | .b.quad ⟱ 950 | .b.stop ⤓ 951 | .b.stroked ⇩ 952 | .b.struck ⤈ 953 | .b.dstruck ⇟ 954 | .b.triple ⤋ 955 | .b.twohead ↡ 956 | .l.r ↔\vs{text} 957 | .l.r.double ⇔ 958 | .l.r.double.long ⟺ 959 | .l.r.double.not ⇎ 960 | .l.r.double.struck ⤄ 961 | .l.r.filled ⬌ 962 | .l.r.long ⟷ 963 | .l.r.not ↮ 964 | .l.r.stroked ⬄ 965 | .l.r.struck ⇹ 966 | .l.r.dstruck ⇼ 967 | .l.r.open ⇿ 968 | .l.r.wave ↭ 969 | .t.b ↕\vs{text} 970 | .t.b.double ⇕ 971 | .t.b.filled ⬍ 972 | .t.b.stroked ⇳ 973 | .tr ↗\vs{text} 974 | .tr.double ⇗ 975 | .tr.filled ⬈ 976 | .tr.hook ⤤ 977 | .tr.stroked ⬀ 978 | .br ↘\vs{text} 979 | .br.double ⇘ 980 | .br.filled ⬊ 981 | .br.hook ⤥ 982 | .br.stroked ⬂ 983 | .tl ↖\vs{text} 984 | .tl.double ⇖ 985 | .tl.filled ⬉ 986 | .tl.hook ⤣ 987 | .tl.stroked ⬁ 988 | .bl ↙\vs{text} 989 | .bl.double ⇙ 990 | .bl.filled ⬋ 991 | .bl.hook ⤦ 992 | .bl.stroked ⬃ 993 | .tl.br ⤡ 994 | .tr.bl ⤢ 995 | .ccw ↺ 996 | .ccw.half ↶ 997 | .cw ↻ 998 | .cw.half ↷ 999 | .zigzag ↯ 1000 | arrows 1001 | .rr ⇉ 1002 | .ll ⇇ 1003 | .tt ⇈ 1004 | .bb ⇊ 1005 | .lr ⇆ 1006 | .lr.stop ↹ 1007 | .rl ⇄ 1008 | .tb ⇅ 1009 | .bt ⇵ 1010 | .rrr ⇶ 1011 | .lll ⬱ 1012 | arrowhead 1013 | .t ⌃ 1014 | .b ⌄ 1015 | harpoon 1016 | .rt ⇀ 1017 | .rt.bar ⥛ 1018 | .rt.stop ⥓ 1019 | .rb ⇁ 1020 | .rb.bar ⥟ 1021 | .rb.stop ⥗ 1022 | .lt ↼ 1023 | .lt.bar ⥚ 1024 | .lt.stop ⥒ 1025 | .lb ↽ 1026 | .lb.bar ⥞ 1027 | .lb.stop ⥖ 1028 | .tl ↿ 1029 | .tl.bar ⥠ 1030 | .tl.stop ⥘ 1031 | .tr ↾ 1032 | .tr.bar ⥜ 1033 | .tr.stop ⥔ 1034 | .bl ⇃ 1035 | .bl.bar ⥡ 1036 | .bl.stop ⥙ 1037 | .br ⇂ 1038 | .br.bar ⥝ 1039 | .br.stop ⥕ 1040 | .lt.rt ⥎ 1041 | .lb.rb ⥐ 1042 | .lb.rt ⥋ 1043 | .lt.rb ⥊ 1044 | .tl.bl ⥑ 1045 | .tr.br ⥏ 1046 | .tl.br ⥍ 1047 | .tr.bl ⥌ 1048 | harpoons 1049 | .rtrb ⥤ 1050 | .blbr ⥥ 1051 | .bltr ⥯ 1052 | .lbrb ⥧ 1053 | .ltlb ⥢ 1054 | .ltrb ⇋ 1055 | .ltrt ⥦ 1056 | .rblb ⥩ 1057 | .rtlb ⇌ 1058 | .rtlt ⥨ 1059 | .tlbr ⥮ 1060 | .tltr ⥣ 1061 | tack 1062 | .r ⊢ 1063 | .r.not ⊬ 1064 | .r.long ⟝ 1065 | .r.short ⊦ 1066 | .r.double ⊨ 1067 | .r.double.not ⊭ 1068 | .l ⊣ 1069 | .l.long ⟞ 1070 | .l.short ⫞ 1071 | .l.double ⫤ 1072 | .t ⊥ 1073 | .t.big ⟘ 1074 | .t.double ⫫ 1075 | .t.short ⫠ 1076 | .b ⊤ 1077 | .b.big ⟙ 1078 | .b.double ⫪ 1079 | .b.short ⫟ 1080 | .l.r ⟛ 1081 | 1082 | // Lowercase Greek. 1083 | alpha α 1084 | beta β 1085 | .alt ϐ 1086 | chi χ 1087 | delta δ 1088 | digamma ϝ 1089 | epsilon ε 1090 | .alt ϵ 1091 | .alt.rev ϶ 1092 | eta η 1093 | gamma γ 1094 | iota ι 1095 | .inv ℩ 1096 | kappa κ 1097 | .alt ϰ 1098 | lambda λ 1099 | mu μ 1100 | nu ν 1101 | omega ω 1102 | omicron ο 1103 | phi φ 1104 | .alt ϕ 1105 | pi π 1106 | .alt ϖ 1107 | psi ψ 1108 | rho ρ 1109 | .alt ϱ 1110 | sigma σ 1111 | .alt ς 1112 | tau τ 1113 | theta θ 1114 | .alt ϑ 1115 | upsilon υ 1116 | xi ξ 1117 | zeta ζ 1118 | 1119 | // Uppercase Greek. 1120 | Alpha Α 1121 | Beta Β 1122 | Chi Χ 1123 | Delta Δ 1124 | Digamma Ϝ 1125 | Epsilon Ε 1126 | Eta Η 1127 | Gamma Γ 1128 | Iota Ι 1129 | Kappa Κ 1130 | Lambda Λ 1131 | Mu Μ 1132 | Nu Ν 1133 | Omega Ω 1134 | .inv ℧ 1135 | Omicron Ο 1136 | Phi Φ 1137 | Pi Π 1138 | Psi Ψ 1139 | Rho Ρ 1140 | Sigma Σ 1141 | Tau Τ 1142 | Theta Θ 1143 | .alt ϴ 1144 | Upsilon Υ 1145 | Xi Ξ 1146 | Zeta Ζ 1147 | 1148 | // Lowercase Cyrillic. 1149 | sha ш 1150 | 1151 | // Uppercase Cyrillic. 1152 | Sha Ш 1153 | 1154 | // Hebrew. 1155 | // In math, the following symbols are replaced with corresponding characters 1156 | // from Letterlike Symbols. 1157 | // See https://github.com/typst/typst/pull/3375. 1158 | aleph א 1159 | beth ב 1160 | gimel ג 1161 | daleth ד 1162 | 1163 | // Double-struck. 1164 | AA 𝔸 1165 | BB 𝔹 1166 | CC ℂ 1167 | DD 𝔻 1168 | EE 𝔼 1169 | FF 𝔽 1170 | GG 𝔾 1171 | HH ℍ 1172 | II 𝕀 1173 | JJ 𝕁 1174 | KK 𝕂 1175 | LL 𝕃 1176 | MM 𝕄 1177 | NN ℕ 1178 | OO 𝕆 1179 | PP ℙ 1180 | QQ ℚ 1181 | RR ℝ 1182 | SS 𝕊 1183 | TT 𝕋 1184 | UU 𝕌 1185 | VV 𝕍 1186 | WW 𝕎 1187 | XX 𝕏 1188 | YY 𝕐 1189 | ZZ ℤ 1190 | 1191 | // Miscellaneous letter-likes. 1192 | angstrom Å 1193 | ell ℓ 1194 | pee ℘ 1195 | planck ħ 1196 | Re ℜ 1197 | Im ℑ 1198 | dotless 1199 | .i ı 1200 | .j ȷ 1201 | 1202 | // Miscellany. 1203 | die 1204 | .six ⚅ 1205 | .five ⚄ 1206 | .four ⚃ 1207 | .three ⚂ 1208 | .two ⚁ 1209 | .one ⚀ 1210 | errorbar 1211 | .square.stroked ⧮ 1212 | .square.filled ⧯ 1213 | .diamond.stroked ⧰ 1214 | .diamond.filled ⧱ 1215 | .circle.stroked ⧲ 1216 | .circle.filled ⧳ 1217 | 1218 | gender { 1219 | female ♀\vs{text} 1220 | .double ⚢ 1221 | .male ⚤ 1222 | intersex ⚥ 1223 | male ♂\vs{text} 1224 | .double ⚣ 1225 | .female ⚤ 1226 | .stroke ⚦ 1227 | .stroke.t ⚨ 1228 | .stroke.r ⚩ 1229 | neuter ⚲ 1230 | trans ⚧\vs{text} 1231 | } 1232 | -------------------------------------------------------------------------------- /src/modules/emoji.txt: -------------------------------------------------------------------------------- 1 | abacus 🧮 2 | abc 🔤 3 | abcd 🔡 4 | ABCD 🔠 5 | accordion 🪗 6 | aesculapius ⚕\vs{emoji} 7 | airplane ✈\vs{emoji} 8 | .landing 🛬 9 | .small 🛩\vs{emoji} 10 | .takeoff 🛫 11 | alembic ⚗\vs{emoji} 12 | alien 👽\vs{emoji} 13 | .monster 👾 14 | ambulance 🚑\vs{emoji} 15 | amphora 🏺 16 | anchor ⚓\vs{emoji} 17 | anger 💢 18 | ant 🐜 19 | apple 20 | .green 🍏 21 | .red 🍎 22 | arm 23 | .mech 🦾 24 | .muscle 💪 25 | .selfie 🤳 26 | arrow 27 | .r.filled ➡\vs{emoji} 28 | .r.hook ↪\vs{emoji} 29 | .r.soon 🔜 30 | .l.filled ⬅\vs{emoji} 31 | .l.hook ↩\vs{emoji} 32 | .l.back 🔙 33 | .l.end 🔚 34 | .t.filled ⬆\vs{emoji} 35 | .t.curve ⤴\vs{emoji} 36 | .t.top 🔝 37 | .b.filled ⬇\vs{emoji} 38 | .b.curve ⤵\vs{emoji} 39 | .l.r ↔\vs{emoji} 40 | .l.r.on 🔛 41 | .t.b ↕\vs{emoji} 42 | .bl ↙\vs{emoji} 43 | .br ↘\vs{emoji} 44 | .tl ↖\vs{emoji} 45 | .tr ↗\vs{emoji} 46 | arrows 47 | .cycle 🔄 48 | ast *\vs{emoji} 49 | .box ✳\vs{emoji} 50 | atm 🏧 51 | atom ⚛\vs{emoji} 52 | aubergine 🍆 53 | avocado 🥑 54 | axe 🪓 55 | baby 👶 56 | .angel 👼 57 | .box 🚼\vs{emoji} 58 | babybottle 🍼 59 | backpack 🎒 60 | bacon 🥓 61 | badger 🦡 62 | badminton 🏸 63 | bagel 🥯 64 | baggageclaim 🛄 65 | baguette 🥖 66 | balloon 🎈 67 | ballot 68 | .check ☑\vs{emoji} 69 | ballotbox 🗳\vs{emoji} 70 | banana 🍌 71 | banjo 🪕 72 | bank 🏦 73 | barberpole 💈 74 | baseball ⚾\vs{emoji} 75 | basecap 🧢 76 | basket 🧺 77 | basketball ⛹\vs{emoji} 78 | .ball 🏀 79 | bat 🦇 80 | bathtub 🛀 81 | .foam 🛁 82 | battery 🔋 83 | .low 🪫 84 | beach 85 | .palm 🏝\vs{emoji} 86 | .umbrella 🏖\vs{emoji} 87 | beads 📿 88 | beans 🫘 89 | bear 🐻 90 | beaver 🦫 91 | bed 🛏\vs{emoji} 92 | .person 🛌 93 | bee 🐝 94 | beer 🍺 95 | .clink 🍻 96 | beet 🫜 97 | beetle 🪲 98 | .lady 🐞 99 | bell 🔔 100 | .ding 🛎\vs{emoji} 101 | .not 🔕 102 | bento 🍱 103 | bicyclist 🚴 104 | .mountain 🚵 105 | bigfoot 🫈 106 | bike 🚲\vs{emoji} 107 | .not 🚳 108 | bikini 👙 109 | billiards 🎱 110 | bin 🗑\vs{emoji} 111 | biohazard ☣\vs{emoji} 112 | bird 🐦\vs{emoji} 113 | bison 🦬 114 | blood 🩸 115 | blouse 👚 116 | blowfish 🐡 117 | blueberries 🫐 118 | boar 🐗 119 | boat 120 | .sail ⛵\vs{emoji} 121 | .row 🚣 122 | .motor 🛥\vs{emoji} 123 | .speed 🚤 124 | .canoe 🛶 125 | bolt 🔩 126 | bomb 💣\vs{emoji} 127 | bone 🦴 128 | book 129 | .red 📕 130 | .blue 📘 131 | .green 📗 132 | .orange 📙 133 | .spiral 📒 134 | .open 📖 135 | bookmark 🔖 136 | books 📚\vs{emoji} 137 | boomerang 🪃 138 | bordercontrol 🛂 139 | bouquet 💐 140 | bow 🏹 141 | bowl 142 | .spoon 🥣 143 | .steam 🍜 144 | bowling 🎳 145 | boxing 🥊 146 | boy 👦 147 | brain 🧠 148 | bread 🍞 149 | brick 🧱 150 | bride 👰 151 | bridge 152 | .fog 🌁 153 | .night 🌉 154 | briefcase 💼 155 | briefs 🩲 156 | brightness 157 | .high 🔆 158 | .low 🔅 159 | broccoli 🥦 160 | broom 🧹 161 | brush 🖌\vs{emoji} 162 | bubble 163 | .speech.r 💬 164 | .speech.l 🗨\vs{emoji} 165 | .thought 💭 166 | .anger.r 🗯\vs{emoji} 167 | bubbles 🫧 168 | bubbletea 🧋 169 | bucket 🪣 170 | buffalo 171 | .water 🐃 172 | bug 🐛 173 | builder 👷 174 | burger 🍔 175 | burrito 🌯 176 | bus 🚌 177 | .front 🚍\vs{emoji} 178 | .small 🚐 179 | .stop 🚏 180 | .trolley 🚎 181 | butter 🧈 182 | butterfly 🦋 183 | button 🔲 184 | .alt 🔳 185 | .radio 🔘 186 | cabinet 187 | .file 🗄\vs{emoji} 188 | cablecar 🚠 189 | .small 🚡 190 | cactus 🌵 191 | cake 🎂 192 | .fish 🍥 193 | .moon 🥮 194 | .slice 🍰 195 | calendar 📅 196 | .spiral 🗓\vs{emoji} 197 | .tearoff 📆 198 | camel 🐫 199 | .dromedar 🐪 200 | camera 📷\vs{emoji} 201 | .flash 📸 202 | .movie 🎥 203 | .movie.box 🎦 204 | .video 📹\vs{emoji} 205 | camping 🏕\vs{emoji} 206 | can 🥫 207 | candle 🕯\vs{emoji} 208 | candy 🍬 209 | cane 🦯 210 | car 🚗 211 | .front 🚘\vs{emoji} 212 | .pickup 🛻 213 | .police 🚓 214 | .police.front 🚔\vs{emoji} 215 | .racing 🏎\vs{emoji} 216 | .rickshaw 🛺 217 | .suv 🚙 218 | card 219 | .credit 💳\vs{emoji} 220 | .id 🪪 221 | cardindex 📇 222 | carrot 🥕 223 | cart 🛒 224 | cassette 📼 225 | castle 226 | .eu 🏰 227 | .jp 🏯 228 | cat 🐈\vs{emoji} 229 | .face 🐱 230 | .face.angry 😾 231 | .face.cry 😿 232 | .face.heart 😻 233 | .face.joy 😹 234 | .face.kiss 😽 235 | .face.laugh 😸 236 | .face.shock 🙀 237 | .face.smile 😺 238 | .face.smirk 😼 239 | chain 🔗 240 | chains ⛓\vs{emoji} 241 | chair 🪑 242 | champagne 🍾 243 | chart 244 | .bar 📊 245 | .up 📈 246 | .down 📉 247 | .yen.up 💹 248 | checkmark 249 | .heavy ✔\vs{emoji} 250 | .box ✅\vs{emoji} 251 | cheese 🧀 252 | cherries 🍒 253 | chess ♟\vs{emoji} 254 | chestnut 🌰 255 | chicken 🐔 256 | .baby 🐥 257 | .baby.egg 🐣 258 | .baby.head 🐤 259 | .leg 🍗 260 | .male 🐓 261 | child 🧒 262 | chipmunk 🐿\vs{emoji} 263 | chocolate 🍫 264 | chopsticks 🥢 265 | church ⛪\vs{emoji} 266 | .love 💒 267 | cigarette 🚬 268 | .not 🚭\vs{emoji} 269 | circle 270 | .black ⚫\vs{emoji} 271 | .blue 🔵 272 | .brown 🟤 273 | .green 🟢 274 | .orange 🟠 275 | .purple 🟣 276 | .white ⚪\vs{emoji} 277 | .red 🔴 278 | .yellow 🟡 279 | .stroked ⭕\vs{emoji} 280 | circus 🎪 281 | city 🏙\vs{emoji} 282 | .dusk 🌆 283 | .night 🌃 284 | .sunset 🌇 285 | clamp 🗜\vs{emoji} 286 | clapperboard 🎬\vs{emoji} 287 | climbing 🧗 288 | clip 📎 289 | clipboard 📋\vs{emoji} 290 | clips 🖇\vs{emoji} 291 | clock 292 | .one 🕐\vs{emoji} 293 | .one.thirty 🕜\vs{emoji} 294 | .two 🕑\vs{emoji} 295 | .two.thirty 🕝\vs{emoji} 296 | .three 🕒\vs{emoji} 297 | .three.thirty 🕞\vs{emoji} 298 | .four 🕓\vs{emoji} 299 | .four.thirty 🕟\vs{emoji} 300 | .five 🕔\vs{emoji} 301 | .five.thirty 🕠\vs{emoji} 302 | .six 🕕\vs{emoji} 303 | .six.thirty 🕡\vs{emoji} 304 | .seven 🕖\vs{emoji} 305 | .seven.thirty 🕢\vs{emoji} 306 | .eight 🕗\vs{emoji} 307 | .eight.thirty 🕣\vs{emoji} 308 | .nine 🕘\vs{emoji} 309 | .nine.thirty 🕤\vs{emoji} 310 | .ten 🕙\vs{emoji} 311 | .ten.thirty 🕥\vs{emoji} 312 | .eleven 🕚\vs{emoji} 313 | .eleven.thirty 🕦\vs{emoji} 314 | .twelve 🕛\vs{emoji} 315 | .twelve.thirty 🕧\vs{emoji} 316 | .alarm ⏰\vs{emoji} 317 | .old 🕰\vs{emoji} 318 | .timer ⏲\vs{emoji} 319 | cloud ☁\vs{emoji} 320 | .dust 💨 321 | .rain 🌧\vs{emoji} 322 | .snow 🌨\vs{emoji} 323 | .storm ⛈\vs{emoji} 324 | .sun ⛅\vs{emoji} 325 | .sun.hidden 🌥\vs{emoji} 326 | .sun.rain 🌦\vs{emoji} 327 | .thunder 🌩\vs{emoji} 328 | coat 🧥 329 | .lab 🥼 330 | cockroach 🪳 331 | cocktail 332 | .martini 🍸\vs{emoji} 333 | .tropical 🍹 334 | coconut 🥥 335 | coffee ☕\vs{emoji} 336 | coffin ⚰\vs{emoji} 337 | coin 🪙 338 | comet ☄\vs{emoji} 339 | compass 🧭 340 | computer 🖥\vs{emoji} 341 | computermouse 🖱\vs{emoji} 342 | confetti 🎊 343 | construction 🚧 344 | controller 🎮\vs{emoji} 345 | cookie 🍪 346 | .fortune 🥠 347 | cooking 🍳 348 | cool 🆒 349 | copyright ©\vs{emoji} 350 | coral 🪸 351 | corn 🌽 352 | couch 🛋\vs{emoji} 353 | couple 💑 354 | cow 🐄 355 | .face 🐮 356 | crab 🦀 357 | crane 🏗\vs{emoji} 358 | crayon 🖍\vs{emoji} 359 | cricket 🦗 360 | cricketbat 🏏 361 | crocodile 🐊 362 | croissant 🥐 363 | crossmark ❌\vs{emoji} 364 | .box ❎\vs{emoji} 365 | crown 👑 366 | crutch 🩼 367 | crystal 🔮 368 | cucumber 🥒 369 | cup 370 | .straw 🥤 371 | cupcake 🧁 372 | curling 🥌 373 | curry 🍛 374 | custard 🍮 375 | customs 🛃 376 | cutlery 🍴 377 | cyclone 🌀 378 | dancing 379 | .ballet 🧑‍🩰 380 | .man 🕺 381 | .woman 💃 382 | .bunny 👯 383 | .bunny.men 👯‍♂ 384 | .bunny.women 👯‍♀ 385 | darts 🎯 386 | dash 387 | .wave.double 〰\vs{emoji} 388 | deer 🦌 389 | desert 🏜\vs{emoji} 390 | detective 🕵\vs{emoji} 391 | diamond 392 | .blue 🔷 393 | .blue.small 🔹 394 | .orange 🔶 395 | .orange.small 🔸 396 | .dot 💠 397 | die 🎲 398 | dino 399 | .pod 🦕 400 | .rex 🦖 401 | disc 402 | .cd 💿\vs{emoji} 403 | .dvd 📀 404 | .mini 💽 405 | discoball 🪩 406 | diving 🤿 407 | dodo 🦤 408 | dog 🐕\vs{emoji} 409 | .face 🐶 410 | .guide 🦮 411 | .poodle 🐩 412 | dollar 💲 413 | dolphin 🐬 414 | donkey 🫏 415 | donut 🍩 416 | door 🚪 417 | dove 418 | .peace 🕊\vs{emoji} 419 | dragon 🐉 420 | .face 🐲 421 | dress 👗 422 | .kimono 👘 423 | .sari 🥻 424 | drop 💧 425 | drops 💦 426 | drum 🥁 427 | .big 🪘 428 | duck 🦆 429 | dumpling 🥟 430 | eagle 🦅 431 | ear 👂\vs{emoji} 432 | .aid 🦻 433 | egg 🥚 434 | eighteen 435 | .not 🔞 436 | elephant 🐘 437 | elevator 🛗 438 | elf 🧝 439 | email 📧 440 | excl ❗\vs{emoji} 441 | .white ❕\vs{emoji} 442 | .double ‼\vs{emoji} 443 | .quest ⁉\vs{emoji} 444 | explosion 💥 445 | extinguisher 🧯 446 | eye 👁\vs{emoji} 447 | eyes 👀 448 | face 449 | .grin 😀 450 | .angry 😠 451 | .angry.red 😡 452 | .anguish 😧 453 | .astonish 😲 454 | .bandage 🤕 455 | .beam 😁 456 | .blank 😶 457 | .clown 🤡 458 | .cold 🥶 459 | .concern 😦 460 | .cool 😎 461 | .cover 🤭 462 | .cowboy 🤠 463 | .cry 😭 464 | .devil.smile 😈 465 | .devil.frown 👿 466 | .diagonal 🫤 467 | .disguise 🥸 468 | .distorted 🫪 469 | .distress 😫 470 | .dizzy 😵 471 | .dotted 🫥 472 | .down 😞 473 | .down.sweat 😓 474 | .drool 🤤 475 | .explode 🤯 476 | .eyeroll 🙄 477 | .friendly ☺\vs{emoji} 478 | .fear 😨 479 | .fear.sweat 😰 480 | .fever 🤒 481 | .flush 😳 482 | .frown ☹\vs{emoji} 483 | .frown.slight 🙁 484 | .frust 😣 485 | .goofy 🤪 486 | .halo 😇 487 | .happy 😊 488 | .heart 😍 489 | .hearts 🥰 490 | .heat 🥵 491 | .hug 🤗 492 | .inv 🙃 493 | .joy 😂 494 | .kiss 😗 495 | .kiss.smile 😙 496 | .kiss.heart 😘 497 | .kiss.blush 😚 498 | .lick 😋 499 | .lie 🤥 500 | .mask 😷 501 | .meh 😒 502 | .melt 🫠 503 | .money 🤑 504 | .monocle 🧐 505 | .nausea 🤢 506 | .nerd 🤓 507 | .neutral 😐\vs{emoji} 508 | .open 😃 509 | .party 🥳 510 | .peek 🫣 511 | .plead 🥺 512 | .relief 😌 513 | .rofl 🤣 514 | .sad 😔 515 | .salute 🫡 516 | .shaking 🫨 517 | .shock 😱 518 | .shush 🤫 519 | .skeptic 🤨 520 | .sleep 😴 521 | .sleepy 😪 522 | .smile 😄 523 | .smile.slight 🙂 524 | .smile.sweat 😅 525 | .smile.tear 🥲 526 | .smirk 😏 527 | .sneeze 🤧 528 | .speak.not 🫢 529 | .squint 😆 530 | .stars 🤩 531 | .straight 😑 532 | .suffer 😖 533 | .surprise 😯 534 | .symbols 🤬 535 | .tear 😢 536 | .tear.relief 😥 537 | .tear.withheld 🥹 538 | .teeth 😬 539 | .think 🤔 540 | .tired 🫩 541 | .tongue 😛 542 | .tongue.squint 😝 543 | .tongue.wink 😜 544 | .triumph 😤 545 | .unhappy 😕 546 | .vomit 🤮 547 | .weary 😩 548 | .wink 😉 549 | .woozy 🥴 550 | .worry 😟 551 | .wow 😮 552 | .yawn 🥱 553 | .zip 🤐 554 | factory 🏭\vs{emoji} 555 | fairy 🧚 556 | faith 557 | .christ ✝\vs{emoji} 558 | .dharma ☸\vs{emoji} 559 | .khanda 🪯 560 | .islam ☪\vs{emoji} 561 | .judaism ✡\vs{emoji} 562 | .menorah 🕎 563 | .om 🕉\vs{emoji} 564 | .orthodox ☦\vs{emoji} 565 | .peace ☮\vs{emoji} 566 | .star.dot 🔯 567 | .worship 🛐 568 | .yinyang ☯\vs{emoji} 569 | falafel 🧆 570 | family 👪\vs{emoji} 571 | fax 📠 572 | feather 🪶 573 | feeding 574 | .breast 🤱 575 | fencing 🤺 576 | ferriswheel 🎡 577 | fightcloud 🫯 578 | filebox 🗃\vs{emoji} 579 | filedividers 🗂\vs{emoji} 580 | film 🎞\vs{emoji} 581 | finger 582 | .r 👉\vs{emoji} 583 | .l 👈\vs{emoji} 584 | .t 👆\vs{emoji} 585 | .t.alt ☝\vs{emoji} 586 | .b 👇\vs{emoji} 587 | .front 🫵 588 | .m 🖕 589 | fingerprint 🫆 590 | fingers 591 | .cross 🤞 592 | .pinch 🤌 593 | .snap 🫰 594 | fire 🔥 595 | firecracker 🧨 596 | fireengine 🚒 597 | fireworks 🎆 598 | fish 🐟\vs{emoji} 599 | .tropical 🐠 600 | fishing 🎣 601 | fist 602 | .front 👊 603 | .r 🤜 604 | .l 🤛 605 | .raised ✊\vs{emoji} 606 | flag 607 | .black 🏴 608 | .white 🏳\vs{emoji} 609 | .goal 🏁 610 | .golf ⛳\vs{emoji} 611 | .red 🚩 612 | flags 613 | .jp.crossed 🎌 614 | flamingo 🦩 615 | flashlight 🔦 616 | flatbread 🫓 617 | fleur ⚜\vs{emoji} 618 | floppy 💾 619 | flower 620 | .hibiscus 🌺 621 | .hyacinth 🪻 622 | .lotus 🪷 623 | .pink 🌸 624 | .rose 🌹 625 | .sun 🌻 626 | .tulip 🌷 627 | .white 💮 628 | .wilted 🥀 629 | .yellow 🌼 630 | flute 🪈 631 | fly 🪰 632 | fog 🌫\vs{emoji} 633 | folder 📁 634 | .open 📂 635 | fondue 🫕 636 | foot 🦶 637 | football ⚽\vs{emoji} 638 | .am 🏈 639 | forex 💱 640 | fountain ⛲\vs{emoji} 641 | fox 🦊 642 | free 🆓 643 | fries 🍟 644 | frisbee 🥏 645 | frog 646 | .face 🐸 647 | fuelpump ⛽\vs{emoji} 648 | garlic 🧄 649 | gear ⚙\vs{emoji} 650 | gem 💎 651 | genie 🧞 652 | ghost 👻 653 | ginger 🫚 654 | giraffe 🦒 655 | girl 👧 656 | glass 657 | .clink 🥂 658 | .milk 🥛 659 | .pour 🫗 660 | .tumbler 🥃 661 | glasses 👓\vs{emoji} 662 | .sun 🕶\vs{emoji} 663 | globe 664 | .am 🌎\vs{emoji} 665 | .as.au 🌏\vs{emoji} 666 | .eu.af 🌍\vs{emoji} 667 | .meridian 🌐 668 | gloves 🧤 669 | goal 🥅 670 | goat 🐐 671 | goggles 🥽 672 | golfing 🏌\vs{emoji} 673 | goose 🪿 674 | gorilla 🦍 675 | grapes 🍇 676 | guard 677 | .man 💂 678 | guitar 🎸 679 | gymnastics 🤸 680 | haircut 💇 681 | hairpick 🪮 682 | hammer 🔨 683 | .pick ⚒\vs{emoji} 684 | .wrench 🛠\vs{emoji} 685 | hamsa 🪬 686 | hamster 687 | .face 🐹 688 | hand 689 | .raised ✋\vs{emoji} 690 | .raised.alt 🤚 691 | .r 🫱 692 | .l 🫲 693 | .t 🫴 694 | .b 🫳 695 | .ok 👌 696 | .call 🤙 697 | .love 🤟 698 | .part 🖖 699 | .peace ✌\vs{emoji} 700 | .pinch 🤏 701 | .pushing.l 🫷 702 | .pushing.r 🫸 703 | .rock 🤘 704 | .splay 🖐\vs{emoji} 705 | .wave 👋 706 | .write ✍\vs{emoji} 707 | handbag 👜 708 | handball 🤾 709 | handfan 🪭 710 | handholding 🧑‍🤝‍🧑 711 | .man.man 👬 712 | .woman.man 👫 713 | .woman.woman 👭 714 | hands 715 | .folded 🙏 716 | .palms 🤲 717 | .clap 👏 718 | .heart 🫶 719 | .open 👐 720 | .raised 🙌 721 | .shake 🤝 722 | harp 🪉 723 | hash #\vs{emoji} 724 | hat 725 | .ribbon 👒 726 | .top 🎩 727 | headphone 🎧\vs{emoji} 728 | heart ❤\vs{emoji} 729 | .arrow 💘 730 | .beat 💓 731 | .black 🖤 732 | .blue 💙 733 | .box 💟 734 | .broken 💔 735 | .brown 🤎 736 | .double 💕 737 | .excl ❣\vs{emoji} 738 | .gray 🩶 739 | .green 💚 740 | .grow 💗 741 | .lightblue 🩵 742 | .orange 🧡 743 | .pink 🩷 744 | .purple 💜 745 | .real 🫀 746 | .revolve 💞 747 | .ribbon 💝 748 | .spark 💖 749 | .white 🤍 750 | .yellow 💛 751 | hedgehog 🦔 752 | helicopter 🚁 753 | helix 🧬 754 | helmet 755 | .cross ⛑\vs{emoji} 756 | .military 🪖 757 | hippo 🦛 758 | hockey 🏑 759 | hole 🕳\vs{emoji} 760 | honey 🍯 761 | hongbao 🧧 762 | hook 🪝 763 | horn 764 | .postal 📯 765 | horse 🐎 766 | .carousel 🎠 767 | .face 🐴 768 | .race 🏇 769 | hospital 🏥 770 | hotdog 🌭 771 | hotel 🏨 772 | .love 🏩 773 | hotspring ♨\vs{emoji} 774 | hourglass ⌛\vs{emoji} 775 | .flow ⏳\vs{emoji} 776 | house 🏠\vs{emoji} 777 | .derelict 🏚\vs{emoji} 778 | .garden 🏡 779 | .multiple 🏘\vs{emoji} 780 | hundred 💯 781 | hut 🛖 782 | ice 🧊 783 | icecream 🍨 784 | .shaved 🍧 785 | .soft 🍦 786 | icehockey 🏒 787 | id 🆔 788 | info ℹ\vs{emoji} 789 | izakaya 🏮 790 | jar 🫙 791 | jellyfish 🪼 792 | jeans 👖 793 | jigsaw 🧩 794 | joystick 🕹\vs{emoji} 795 | juggling 🤹 796 | juice 🧃 797 | kaaba 🕋 798 | kadomatsu 🎍 799 | kangaroo 🦘 800 | gachi 🈷\vs{emoji} 801 | go 🈴 802 | hi ㊙\vs{emoji} 803 | ka 🉑 804 | kachi 🈹 805 | kara 🈳 806 | kon 🈲 807 | man 👨 808 | .box 🚹\vs{emoji} 809 | .crown 🤴 810 | .guapimao 👲 811 | .levitate 🕴\vs{emoji} 812 | .old 👴 813 | .pregnant 🫃 814 | .turban 👳 815 | .tuxedo 🤵 816 | muryo 🈚\vs{emoji} 817 | shin 🈸 818 | shuku ㊗\vs{emoji} 819 | toku 🉐 820 | yo 🈺 821 | yubi 🈯\vs{emoji} 822 | yuryo 🈶 823 | koko 🈁 824 | sa 🈂\vs{emoji} 825 | kebab 🥙 826 | key 🔑 827 | .old 🗝\vs{emoji} 828 | keyboard ⌨\vs{emoji} 829 | kiss 💏 830 | kissmark 💋 831 | kite 🪁 832 | kiwi 🥝 833 | knife 🔪 834 | .dagger 🗡\vs{emoji} 835 | knot 🪢 836 | koala 🐨 837 | koinobori 🎏 838 | label 🏷\vs{emoji} 839 | lacrosse 🥍 840 | ladder 🪜 841 | lamp 842 | .diya 🪔 843 | landslide 🛘 844 | laptop 💻\vs{emoji} 845 | a 🅰\vs{emoji} 846 | ab 🆎 847 | b 🅱\vs{emoji} 848 | cl 🆑 849 | o 🅾\vs{emoji} 850 | leaf 851 | .clover.three ☘\vs{emoji} 852 | .clover.four 🍀 853 | .fall 🍂 854 | .herb 🌿 855 | .maple 🍁 856 | .wind 🍃 857 | leftluggage 🛅 858 | leg 🦵 859 | .mech 🦿 860 | lemon 🍋 861 | leopard 🐆 862 | letter 863 | .love 💌 864 | liberty 🗽 865 | lightbulb 💡 866 | lightning ⚡\vs{emoji} 867 | lion 🦁 868 | lipstick 💄 869 | litter 🚮 870 | .not 🚯 871 | lizard 🦎 872 | llama 🦙 873 | lobster 🦞 874 | lock 🔒\vs{emoji} 875 | .key 🔐 876 | .open 🔓\vs{emoji} 877 | .pen 🔏 878 | lollipop 🍭 879 | lotion 🧴 880 | luggage 🧳 881 | lungs 🫁 882 | mage 🧙 883 | magnet 🧲 884 | magnify 885 | .r 🔎 886 | .l 🔍\vs{emoji} 887 | mahjong 888 | .dragon.red 🀄\vs{emoji} 889 | mail ✉\vs{emoji} 890 | .arrow 📩 891 | mailbox 892 | .closed.empty 📪\vs{emoji} 893 | .closed.full 📫\vs{emoji} 894 | .open.empty 📭\vs{emoji} 895 | .open.full 📬\vs{emoji} 896 | mammoth 🦣 897 | mango 🥭 898 | map 899 | .world 🗺\vs{emoji} 900 | .jp 🗾 901 | maracas 🪇 902 | martialarts 🥋 903 | masks 🎭\vs{emoji} 904 | mate 🧉 905 | matryoshka 🪆 906 | meat 🥩 907 | .bone 🍖 908 | medal 909 | .first 🥇 910 | .second 🥈 911 | .third 🥉 912 | .sports 🏅 913 | .military 🎖\vs{emoji} 914 | megaphone 📢 915 | .simple 📣 916 | melon 🍈 917 | merperson 🧜 918 | metro Ⓜ\vs{emoji} 919 | microbe 🦠 920 | microphone 🎤 921 | .studio 🎙\vs{emoji} 922 | microscope 🔬 923 | milkyway 🌌 924 | mirror 🪞 925 | mixer 🎛\vs{emoji} 926 | money 927 | .bag 💰\vs{emoji} 928 | .dollar 💵 929 | .euro 💶 930 | .pound 💷 931 | .yen 💴 932 | .wings 💸 933 | monkey 🐒 934 | .face 🐵 935 | .hear.not 🙉 936 | .see.not 🙈 937 | .speak.not 🙊 938 | moon 939 | .crescent 🌙 940 | .full 🌕\vs{emoji} 941 | .full.face 🌝 942 | .new 🌑 943 | .new.face 🌚 944 | .wane.one 🌖 945 | .wane.two 🌗 946 | .wane.three.face 🌜\vs{emoji} 947 | .wane.three 🌘 948 | .wax.one 🌒 949 | .wax.two 🌓 950 | .wax.two.face 🌛 951 | .wax.three 🌔 952 | moose 🫎 953 | mortarboard 🎓\vs{emoji} 954 | mosque 🕌 955 | mosquito 🦟 956 | motorcycle 🏍\vs{emoji} 957 | motorway 🛣\vs{emoji} 958 | mountain ⛰\vs{emoji} 959 | .fuji 🗻 960 | .snow 🏔\vs{emoji} 961 | .sunrise 🌄 962 | mouse 🐁 963 | .face 🐭 964 | mousetrap 🪤 965 | mouth 👄 966 | .bite 🫦 967 | moyai 🗿 968 | museum 🏛\vs{emoji} 969 | mushroom 🍄 970 | musicalscore 🎼 971 | nails 972 | .polish 💅 973 | namebadge 📛 974 | nazar 🧿 975 | necktie 👔 976 | needle 🪡 977 | nest 978 | .empty 🪹 979 | .eggs 🪺 980 | new 🆕 981 | newspaper 📰 982 | .rolled 🗞\vs{emoji} 983 | ng 🆖 984 | ningyo 🎎 985 | ninja 🥷 986 | noentry ⛔\vs{emoji} 987 | nose 👃 988 | notebook 📓 989 | .deco 📔 990 | notepad 🗒\vs{emoji} 991 | notes 🎵 992 | .triple 🎶 993 | numbers 🔢 994 | octopus 🐙 995 | office 🏢 996 | oil 🛢\vs{emoji} 997 | ok 🆗 998 | olive 🫒 999 | oni 👹 1000 | onion 🧅 1001 | orangutan 🦧 1002 | orca 🫍 1003 | otter 🦦 1004 | owl 🦉 1005 | ox 🐂 1006 | oyster 🦪 1007 | package 📦\vs{emoji} 1008 | paella 🥘 1009 | page 📄 1010 | .curl 📃 1011 | .pencil 📝 1012 | pager 📟\vs{emoji} 1013 | pages 1014 | .tabs 📑 1015 | painting 🖼\vs{emoji} 1016 | palette 🎨 1017 | pancakes 🥞 1018 | panda 🐼 1019 | parachute 🪂 1020 | park 🏞\vs{emoji} 1021 | parking 🅿\vs{emoji} 1022 | parrot 🦜 1023 | partalteration 〽\vs{emoji} 1024 | party 🎉 1025 | peach 🍑 1026 | peacock 🦚 1027 | peanuts 🥜 1028 | peapod 🫛 1029 | pear 🍐 1030 | pedestrian 🚶 1031 | .not 🚷 1032 | pen 1033 | .ball 🖊\vs{emoji} 1034 | .fountain 🖋\vs{emoji} 1035 | pencil ✏\vs{emoji} 1036 | penguin 🐧 1037 | pepper 🫑 1038 | .hot 🌶\vs{emoji} 1039 | person 🧑 1040 | .angry 🙎 1041 | .beard 🧔 1042 | .blonde 👱 1043 | .bow 🙇 1044 | .crown 🫅 1045 | .deaf 🧏 1046 | .facepalm 🤦 1047 | .frown 🙍 1048 | .hijab 🧕 1049 | .kneel 🧎 1050 | .lotus 🧘 1051 | .massage 💆 1052 | .no 🙅 1053 | .ok 🙆 1054 | .old 🧓 1055 | .pregnant 🫄 1056 | .raise 🙋 1057 | .sassy 💁 1058 | .shrug 🤷 1059 | .stand 🧍 1060 | .steam 🧖 1061 | petri 🧫 1062 | phone 📱 1063 | .arrow 📲 1064 | .classic ☎\vs{emoji} 1065 | .not 📵 1066 | .off 📴 1067 | .receiver 📞 1068 | .signal 📶 1069 | .vibrate 📳 1070 | piano 🎹 1071 | pick ⛏\vs{emoji} 1072 | pie 🥧 1073 | pig 🐖 1074 | .face 🐷 1075 | .nose 🐽 1076 | pill 💊 1077 | pin 📌 1078 | .round 📍 1079 | pinata 🪅 1080 | pineapple 🍍 1081 | pingpong 🏓 1082 | pistol 🔫 1083 | pizza 🍕 1084 | placard 🪧 1085 | planet 🪐 1086 | plant 🪴 1087 | plaster 🩹 1088 | plate 1089 | .cutlery 🍽\vs{emoji} 1090 | playback 1091 | .down ⏬\vs{emoji} 1092 | .eject ⏏\vs{emoji} 1093 | .forward ⏩\vs{emoji} 1094 | .pause ⏸\vs{emoji} 1095 | .record ⏺\vs{emoji} 1096 | .repeat 🔁 1097 | .repeat.once 🔂 1098 | .repeat.v 🔃 1099 | .restart ⏮\vs{emoji} 1100 | .rewind ⏪\vs{emoji} 1101 | .shuffle 🔀 1102 | .skip ⏭\vs{emoji} 1103 | .stop ⏹\vs{emoji} 1104 | .toggle ⏯\vs{emoji} 1105 | .up ⏫\vs{emoji} 1106 | playingcard 1107 | .flower 🎴 1108 | .joker 🃏 1109 | plunger 🪠 1110 | policeofficer 👮 1111 | poo 💩 1112 | popcorn 🍿 1113 | post 1114 | .eu 🏤 1115 | .jp 🏣 1116 | postbox 📮 1117 | potato 🥔 1118 | .sweet 🍠 1119 | pouch 👝 1120 | powerplug 🔌 1121 | present 🎁 1122 | pretzel 🥨 1123 | printer 🖨\vs{emoji} 1124 | prints 1125 | .foot 👣 1126 | .paw 🐾 1127 | prohibited 🚫 1128 | projector 📽\vs{emoji} 1129 | pumpkin 1130 | .lantern 🎃 1131 | purse 👛 1132 | quest ❓\vs{emoji} 1133 | .white ❔\vs{emoji} 1134 | rabbit 🐇 1135 | .face 🐰 1136 | raccoon 🦝 1137 | radio 📻\vs{emoji} 1138 | radioactive ☢\vs{emoji} 1139 | railway 🛤\vs{emoji} 1140 | rainbow 🌈 1141 | ram 🐏 1142 | rat 🐀 1143 | razor 🪒 1144 | receipt 🧾 1145 | recycling ♻\vs{emoji} 1146 | reg ®\vs{emoji} 1147 | restroom 🚻 1148 | rhino 🦏 1149 | ribbon 🎀 1150 | .remind 🎗\vs{emoji} 1151 | rice 🍚 1152 | .cracker 🍘 1153 | .ear 🌾 1154 | .onigiri 🍙 1155 | ring 💍 1156 | ringbuoy 🛟 1157 | robot 🤖 1158 | rock 🪨 1159 | rocket 🚀 1160 | rollercoaster 🎢 1161 | rosette 🏵\vs{emoji} 1162 | rugby 🏉 1163 | ruler 📏 1164 | .triangle 📐 1165 | running 🏃 1166 | safetypin 🧷 1167 | safetyvest 🦺 1168 | sake 🍶 1169 | salad 🥗 1170 | salt 🧂 1171 | sandwich 🥪 1172 | santa 1173 | .man 🎅 1174 | .woman 🤶 1175 | satdish 📡 1176 | satellite 🛰\vs{emoji} 1177 | saw 🪚 1178 | saxophone 🎷 1179 | scales ⚖\vs{emoji} 1180 | scarf 🧣 1181 | school 🏫 1182 | scissors ✂\vs{emoji} 1183 | scooter 🛴 1184 | .motor 🛵 1185 | scorpion 🦂 1186 | screwdriver 🪛 1187 | scroll 📜 1188 | seal 🦭 1189 | seat 💺 1190 | seedling 🌱 1191 | shark 🦈 1192 | sheep 🐑 1193 | shell 1194 | .spiral 🐚 1195 | shield 🛡\vs{emoji} 1196 | ship 🚢 1197 | .cruise 🛳\vs{emoji} 1198 | .ferry ⛴\vs{emoji} 1199 | shirt 1200 | .sports 🎽 1201 | .t 👕 1202 | shoe 👞 1203 | .ballet 🩰 1204 | .flat 🥿 1205 | .heel 👠 1206 | .hike 🥾 1207 | .ice ⛸\vs{emoji} 1208 | .roller 🛼 1209 | .sandal.heel 👡 1210 | .ski 🎿 1211 | .sneaker 👟 1212 | .tall 👢 1213 | .thong 🩴 1214 | shopping 🛍\vs{emoji} 1215 | shorts 🩳 1216 | shoshinsha 🔰 1217 | shovel 🪏 1218 | shower 🚿 1219 | shrimp 🦐 1220 | .fried 🍤 1221 | shrine ⛩\vs{emoji} 1222 | sign 1223 | .crossing 🚸 1224 | .stop 🛑 1225 | silhouette 👤 1226 | .double 👥 1227 | .hug 🫂 1228 | .speak 🗣\vs{emoji} 1229 | siren 🚨 1230 | skateboard 🛹 1231 | skewer 1232 | .dango 🍡 1233 | .oden 🍢 1234 | skiing ⛷\vs{emoji} 1235 | skull 💀 1236 | .bones ☠\vs{emoji} 1237 | skunk 🦨 1238 | sled 🛷 1239 | slide 🛝 1240 | slider 🎚\vs{emoji} 1241 | sloth 🦥 1242 | slots 🎰 1243 | snail 🐌 1244 | snake 🐍 1245 | snowboarding 🏂\vs{emoji} 1246 | snowflake ❄\vs{emoji} 1247 | snowman ⛄\vs{emoji} 1248 | .snow ☃\vs{emoji} 1249 | soap 🧼 1250 | socks 🧦 1251 | softball 🥎 1252 | sos 🆘 1253 | soup 🍲 1254 | spaghetti 🍝 1255 | sparkle 1256 | .box ❇\vs{emoji} 1257 | sparkler 🎇 1258 | sparkles ✨\vs{emoji} 1259 | speaker 🔈\vs{emoji} 1260 | .not 🔇 1261 | .wave 🔉 1262 | .waves 🔊 1263 | spider 🕷\vs{emoji} 1264 | spiderweb 🕸\vs{emoji} 1265 | spinach 🥬 1266 | splatter 🫟 1267 | sponge 🧽 1268 | spoon 🥄 1269 | square 1270 | .black ⬛\vs{emoji} 1271 | .black.tiny ▪\vs{emoji} 1272 | .black.small ◾\vs{emoji} 1273 | .black.medium ◼\vs{emoji} 1274 | .white ⬜\vs{emoji} 1275 | .white.tiny ▫\vs{emoji} 1276 | .white.small ◽\vs{emoji} 1277 | .white.medium ◻\vs{emoji} 1278 | .blue 🟦 1279 | .brown 🟫 1280 | .green 🟩 1281 | .orange 🟧 1282 | .purple 🟪 1283 | .red 🟥 1284 | .yellow 🟨 1285 | squid 🦑 1286 | stadium 🏟\vs{emoji} 1287 | star ⭐\vs{emoji} 1288 | .arc 💫 1289 | .box ✴\vs{emoji} 1290 | .glow 🌟 1291 | .shoot 🌠 1292 | stethoscope 🩺 1293 | store 1294 | .big 🏬 1295 | .small 🏪 1296 | strawberry 🍓 1297 | suit 1298 | .club ♣\vs{emoji} 1299 | .diamond ♦\vs{emoji} 1300 | .heart ♥\vs{emoji} 1301 | .spade ♠\vs{emoji} 1302 | sun ☀\vs{emoji} 1303 | .cloud 🌤\vs{emoji} 1304 | .face 🌞 1305 | sunrise 🌅 1306 | superhero 🦸 1307 | supervillain 🦹 1308 | surfing 🏄\vs{emoji} 1309 | sushi 🍣 1310 | swan 🦢 1311 | swimming 🏊\vs{emoji} 1312 | swimsuit 🩱 1313 | swords ⚔\vs{emoji} 1314 | symbols 🔣 1315 | synagogue 🕍 1316 | syringe 💉 1317 | taco 🌮 1318 | takeout 🥡 1319 | tamale 🫔 1320 | tanabata 🎋 1321 | tangerine 🍊 1322 | tap 🚰 1323 | .not 🚱 1324 | taxi 🚕 1325 | .front 🚖 1326 | teacup 🍵 1327 | teapot 🫖 1328 | teddy 🧸 1329 | telescope 🔭 1330 | temple 🛕 1331 | ten 🔟 1332 | tengu 👺 1333 | tennis 🎾 1334 | tent ⛺\vs{emoji} 1335 | testtube 🧪 1336 | thermometer 🌡\vs{emoji} 1337 | thread 🧵 1338 | thumb 1339 | .up 👍\vs{emoji} 1340 | .down 👎\vs{emoji} 1341 | ticket 1342 | .event 🎟\vs{emoji} 1343 | .travel 🎫 1344 | tiger 🐅 1345 | .face 🐯 1346 | tm ™\vs{emoji} 1347 | toilet 🚽 1348 | toiletpaper 🧻 1349 | tomato 🍅 1350 | tombstone 🪦 1351 | tongue 👅 1352 | toolbox 🧰 1353 | tooth 🦷 1354 | toothbrush 🪥 1355 | tornado 🌪\vs{emoji} 1356 | tower 1357 | .tokyo 🗼 1358 | trackball 🖲\vs{emoji} 1359 | tractor 🚜 1360 | trafficlight 1361 | .v 🚦 1362 | .h 🚥 1363 | train 🚆 1364 | .car 🚃 1365 | .light 🚈 1366 | .metro 🚇\vs{emoji} 1367 | .mono 🚝 1368 | .mountain 🚞 1369 | .speed 🚄 1370 | .speed.bullet 🚅 1371 | .steam 🚂 1372 | .stop 🚉 1373 | .suspend 🚟 1374 | .tram 🚊 1375 | .tram.car 🚋 1376 | transgender ⚧\vs{emoji} 1377 | tray 1378 | .inbox 📥\vs{emoji} 1379 | .mail 📨 1380 | .outbox 📤\vs{emoji} 1381 | treasure 🪎 1382 | tree 1383 | .deciduous 🌳 1384 | .evergreen 🌲 1385 | .leafless 🪾 1386 | .palm 🌴 1387 | .xmas 🎄 1388 | triangle 1389 | .r ▶\vs{emoji} 1390 | .l ◀\vs{emoji} 1391 | .t 🔼 1392 | .b 🔽 1393 | .t.red 🔺 1394 | .b.red 🔻 1395 | trident 🔱 1396 | troll 🧌 1397 | trombone 🪊 1398 | trophy 🏆\vs{emoji} 1399 | truck 🚚 1400 | .trailer 🚛 1401 | trumpet 🎺 1402 | tsukimi 🎑 1403 | turkey 🦃 1404 | turtle 🐢 1405 | tv 📺\vs{emoji} 1406 | ufo 🛸 1407 | umbrella 1408 | .open ☂\vs{emoji} 1409 | .closed 🌂 1410 | .rain ☔\vs{emoji} 1411 | .sun ⛱\vs{emoji} 1412 | unicorn 🦄 1413 | unknown 🦳 1414 | up 🆙 1415 | urn ⚱\vs{emoji} 1416 | vampire 🧛 1417 | violin 🎻 1418 | volcano 🌋 1419 | volleyball 🏐 1420 | vs 🆚 1421 | waffle 🧇 1422 | wand 🪄 1423 | warning ⚠\vs{emoji} 1424 | watch ⌚\vs{emoji} 1425 | .stop ⏱\vs{emoji} 1426 | watermelon 🍉 1427 | waterpolo 🤽 1428 | wave 🌊 1429 | wc 🚾 1430 | weightlifting 🏋\vs{emoji} 1431 | whale 🐋 1432 | .spout 🐳 1433 | wheel 🛞 1434 | wheelchair 🦽 1435 | .box ♿\vs{emoji} 1436 | .motor 🦼 1437 | wind 🌬\vs{emoji} 1438 | windchime 🎐 1439 | window 🪟 1440 | wine 🍷 1441 | wing 🪽 1442 | wireless 🛜 1443 | wolf 🐺 1444 | woman 👩 1445 | .box 🚺\vs{emoji} 1446 | .crown 👸 1447 | .old 👵 1448 | .pregnant 🤰 1449 | wood 🪵 1450 | worm 🪱 1451 | wrench 🔧 1452 | wrestling 🤼 1453 | xray 🩻 1454 | yarn 🧶 1455 | yoyo 🪀 1456 | zebra 🦓 1457 | zodiac 1458 | .aquarius ♒\vs{emoji} 1459 | .aries ♈\vs{emoji} 1460 | .cancer ♋\vs{emoji} 1461 | .capri ♑\vs{emoji} 1462 | .gemini ♊\vs{emoji} 1463 | .leo ♌\vs{emoji} 1464 | .libra ♎\vs{emoji} 1465 | .ophi ⛎\vs{emoji} 1466 | .pisces ♓\vs{emoji} 1467 | .sagit ♐\vs{emoji} 1468 | .scorpio ♏\vs{emoji} 1469 | .taurus ♉\vs{emoji} 1470 | .virgo ♍\vs{emoji} 1471 | zombie 🧟 1472 | zzz 💤 1473 | -------------------------------------------------------------------------------- /src/styling.rs: -------------------------------------------------------------------------------- 1 | //! Style mathematical symbols in Unicode. 2 | 3 | use std::fmt::{self, Write}; 4 | use std::iter::FusedIterator; 5 | 6 | /// The version of [Unicode](https://www.unicode.org/) that this version of the 7 | /// styling module is based on. 8 | pub const UNICODE_VERSION: (u8, u8, u8) = (16, 0, 0); 9 | 10 | /// A style for mathematical symbols. 11 | /// 12 | /// Notation in mathematics uses a basic set of characters which can be styled. 13 | /// The following groupings are used in the documentation: 14 | /// - digits: The basic Latin digits 0–9 (U+0030..U+0039). 15 | /// - latin: The basic uppercase and lowercase Latin letters, a–z 16 | /// (U+0061..U+007A) and A–Z (U+0041..U+005A). 17 | /// - greek: The uppercase Greek letters Α–Ω (U+0391..U+03A9), plus nabla ∇ 18 | /// (U+2207) and theta ϴ (U+03F4). The lowercase Greek letters α–ω 19 | /// (U+03B1..U+03C9), plus the partial differential sign ∂ (U+2202), and the 20 | /// glyph variants ϵ (U+03F5), ϑ (U+03D1), ϰ (U+03F0), ϕ (U+03D5), ϱ 21 | /// (U+03F1), ϖ (U+03D6). 22 | /// - arabic: The Arabic letters ا (U+0627), ب (U+0628), ت–غ (U+062A..U+063A), 23 | /// ف–و (U+0641..U+0648), ي (U+064A). 24 | /// - arabic-dotless: The dotless Arabic letter variants ٮ (U+066E), ٯ 25 | /// (U+066F), ڡ (U+06A1), ں (U+06BA) 26 | /// - digamma: The uppercase and lowercase digamma, Ϝ (U+03DC) and ϝ (U+03DD). 27 | /// - dotless: The dotless variants of the lowercase Latin letters i and j, ı 28 | /// (U+0131) and ȷ (U+0237). 29 | /// - hebrew: The Hebrew letters א–ד (U+05D0..U+05D3). 30 | /// 31 | /// Note that some styles support only a subset of a group. The characters each 32 | /// style supports are given in their documentation. 33 | /// 34 | /// # Script style variants 35 | /// 36 | /// There are two widely recognized variants of the script style: chancery and 37 | /// roundhand. They can be distinguished with variation sequences, by using the 38 | /// variation selectors U+FE00 and U+FE01 for chancery and roundhand 39 | /// respectively. These are specified in the [StandardizedVariants.txt] file 40 | /// from the Unicode Character Database. 41 | /// 42 | /// Only the uppercase Latin letters are standardized variation sequences, but 43 | /// the [`Chancery`](MathStyle::Chancery) and 44 | /// [`Roundhand`](MathStyle::Roundhand) styles also support the lowercase Latin 45 | /// letters. In addition, the bold styles 46 | /// [`BoldChancery`](MathStyle::BoldChancery) and 47 | /// [`BoldRoundhand`](MathStyle::BoldRoundhand) are provided, which support 48 | /// both the uppercase and lowercase Latin letters despite not being specified 49 | /// as standardized variation sequences by Unicode. 50 | /// 51 | /// # Shaping 52 | /// 53 | /// The Arabic styles (including those from the 54 | /// [`DoubleStruck`](MathStyle::DoubleStruck) style) are not subject to 55 | /// shaping. However, [`Plain`](MathStyle::Plain) should still be shaped, as 56 | /// the characters are Arabic letters in the Arabic block (U+0600..U+06FF). 57 | /// 58 | /// [StandardizedVariants.txt]: 59 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)] 60 | pub enum MathStyle { 61 | /// Unstyled default. May be serif or sans-serif depending on the font. 62 | #[default] 63 | Plain, 64 | /// Bold style. May be serif or sans-serif depending on the font. 65 | /// 66 | /// Supported characters: digits, latin, greek, digamma. 67 | Bold, 68 | /// Italic style. May be serif or sans-serif depending on the font. 69 | /// 70 | /// Supported characters: latin, greek, dotless, and the extra ħ (U+0127). 71 | Italic, 72 | /// Bold italic style. May be serif or sans-serif depending on the font. 73 | /// 74 | /// Supported characters: latin, greek. 75 | BoldItalic, 76 | /// Script style. May be chancery or roundhand depending on the font. 77 | /// 78 | /// Supported characters: latin. 79 | Script, 80 | /// Bold script style. May be chancery or roundhand depending on the font. 81 | /// 82 | /// Supported characters: latin. 83 | BoldScript, 84 | /// Fraktur style. Also known as black-letter style. 85 | /// 86 | /// Supported characters: latin. 87 | Fraktur, 88 | /// Bold fraktur style. Also known as bold black-letter style. 89 | /// 90 | /// Supported characters: latin. 91 | BoldFraktur, 92 | /// Sans-serif style. 93 | /// 94 | /// Supported characters: digits, latin. 95 | SansSerif, 96 | /// Bold sans-serif style. 97 | /// 98 | /// Supported characters: digits, latin, greek. 99 | SansSerifBold, 100 | /// Italic sans-serif style. 101 | /// 102 | /// Supported characters: latin. 103 | SansSerifItalic, 104 | /// Bold italic sans-serif style. 105 | /// 106 | /// Supported characters: latin, greek. 107 | SansSerifBoldItalic, 108 | /// Monospace style. 109 | /// 110 | /// Supported characters: digits, latin. 111 | Monospace, 112 | /// Isolated style. 113 | /// 114 | /// Supported characters: arabic excluding ه (U+0647), arabic-dotless. 115 | Isolated, 116 | /// Initial style. 117 | /// 118 | /// Supported characters: arabic excluding ا (U+0627), د–ز 119 | /// (U+062F..U+0632), ط (U+0637), ظ (U+0638), و (U+0648). 120 | Initial, 121 | /// Tailed style. 122 | /// 123 | /// Supported characters: arabic excluding ا (U+0627), ب (U+0628), 124 | /// ت (U+062A), ث (U+062B), د–ز (U+062F..U+0632), ط (U+0637), ظ (U+0638), 125 | /// ف (U+0641), ك (U+0643), م (U+0645), ه (U+0647), و (U+0648), and 126 | /// arabic-dotless excluding ٮ (U+066E), ڡ (U+06A1). 127 | Tailed, 128 | /// Stretched style. 129 | /// 130 | /// Supported characters: arabic excluding ا (U+0627), د–ز 131 | /// (U+062F..U+0632), ل (U+0644), و (U+0648), and arabic-dotless excluding 132 | /// ٯ (U+066F), ں (U+06BA). 133 | Stretched, 134 | /// Looped style. 135 | /// 136 | /// Supported characters: arabic excluding ك (U+0643). 137 | Looped, 138 | /// Double-struck style. Also known as open-face style or blackboard-bold 139 | /// style. 140 | /// 141 | /// Supported characters: digits, latin, arabic excluding ا (U+0627), 142 | /// ك (U+0643), ه (U+0647), and the extras ∑ (U+2211), Γ (U+0393), Π 143 | /// (U+03A0), γ (U+03B3), π (U+03C0). 144 | DoubleStruck, 145 | /// Italic double-struck style. Also known as italic open-face style or 146 | /// italic blackboard-bold style. 147 | /// 148 | /// This is an exceptional style as only the following Latin letters are 149 | /// supported: D (U+0044), d (U+0064), e (U+0065), i (U+0069), j (U+006A). 150 | DoubleStruckItalic, 151 | /// Chancery variant of script style. 152 | /// 153 | /// Supported characters: latin. 154 | Chancery, 155 | /// Chancery variant of bold script style. 156 | /// 157 | /// Supported characters: latin. 158 | BoldChancery, 159 | /// Roundhand variant of script style. 160 | /// 161 | /// Supported characters: latin. 162 | Roundhand, 163 | /// Roundhand variant of bold script style. 164 | /// 165 | /// Supported characters: latin. 166 | BoldRoundhand, 167 | /// Hebrew letterlike math symbols. 168 | /// 169 | /// Supported characters: hebrew. 170 | Hebrew, 171 | } 172 | 173 | /// Base [`MathStyle`]s used in Typst. 174 | #[non_exhaustive] 175 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 176 | pub enum MathVariant { 177 | Plain, 178 | Fraktur, 179 | SansSerif, 180 | Monospace, 181 | DoubleStruck, 182 | Chancery, 183 | Roundhand, 184 | } 185 | 186 | impl MathStyle { 187 | /// Selects an appropriate [`MathStyle`] for the given `char`. 188 | /// 189 | /// If `variant` is `None`, then [`Plain`](MathVariant::Plain) is used. If 190 | /// `italic` is `None`, then the TeX auto-italic rules are followed: only 191 | /// Latin and lowercase Greek are italicized. 192 | /// 193 | /// If the combination of inputs leads to a style which does not support 194 | /// the given `char`, then fallback occurs, prioritizing the 195 | /// [`MathVariant`] given. 196 | /// 197 | /// # Examples 198 | /// 199 | /// In the following example, as Greek letters are not supported by 200 | /// [`MathStyle::Fraktur`], the variant falls back to 201 | /// [`Plain`](MathVariant::Plain). Since auto-italic was requested and the 202 | /// given char is lowercase Greek, the selected style is italicized. 203 | /// 204 | /// ``` 205 | /// use codex::styling::{MathStyle, MathVariant}; 206 | /// 207 | /// assert_eq!( 208 | /// MathStyle::BoldItalic, 209 | /// MathStyle::select('α', Some(MathVariant::Fraktur), true, None) 210 | /// ); 211 | /// ``` 212 | /// 213 | /// In this example, the request for bold fell back to `false` as there is 214 | /// no bold double-struck style, and the request for italic fell back to 215 | /// `Some(false)` as [`MathStyle::DoubleStruckItalic`] does not support 216 | /// `'R'`. 217 | /// 218 | /// ``` 219 | /// # use codex::styling::{MathStyle, MathVariant}; 220 | /// assert_eq!( 221 | /// MathStyle::DoubleStruck, 222 | /// MathStyle::select('R', Some(MathVariant::DoubleStruck), true, Some(true)) 223 | /// ); 224 | /// ``` 225 | pub fn select( 226 | c: char, 227 | variant: Option, 228 | bold: bool, 229 | italic: Option, 230 | ) -> MathStyle { 231 | use conversions::*; 232 | use MathVariant::*; 233 | match (variant.unwrap_or(Plain), bold, italic) { 234 | (SansSerif, false, Some(false)) if is_latin(c) => MathStyle::SansSerif, 235 | (SansSerif, false, _) if is_latin(c) => MathStyle::SansSerifItalic, 236 | (SansSerif, true, Some(false)) if is_latin(c) => MathStyle::SansSerifBold, 237 | (SansSerif, true, _) if is_latin(c) => MathStyle::SansSerifBoldItalic, 238 | (SansSerif, false, _) if is_digit(c) => MathStyle::SansSerif, 239 | (SansSerif, true, _) if is_digit(c) => MathStyle::SansSerifBold, 240 | (SansSerif, _, Some(false)) if is_greek(c) => MathStyle::SansSerifBold, 241 | (SansSerif, _, Some(true)) if is_greek(c) => MathStyle::SansSerifBoldItalic, 242 | (SansSerif, _, None) if is_upper_greek(c) => MathStyle::SansSerifBold, 243 | (SansSerif, _, None) if is_lower_greek(c) => MathStyle::SansSerifBoldItalic, 244 | (Fraktur, false, _) if is_latin(c) => MathStyle::Fraktur, 245 | (Fraktur, true, _) if is_latin(c) => MathStyle::BoldFraktur, 246 | (Monospace, _, _) if is_digit(c) | is_latin(c) => MathStyle::Monospace, 247 | (DoubleStruck, _, Some(true)) if matches!(c, 'D' | 'd' | 'e' | 'i' | 'j') => { 248 | MathStyle::DoubleStruckItalic 249 | } 250 | (DoubleStruck, _, _) 251 | if is_digit(c) 252 | | is_latin(c) 253 | | matches!(c, '∑' | 'Γ' | 'Π' | 'γ' | 'π') => 254 | { 255 | MathStyle::DoubleStruck 256 | } 257 | (Chancery, false, _) if is_latin(c) => MathStyle::Chancery, 258 | (Chancery, true, _) if is_latin(c) => MathStyle::BoldChancery, 259 | (Roundhand, false, _) if is_latin(c) => MathStyle::Roundhand, 260 | (Roundhand, true, _) if is_latin(c) => MathStyle::BoldRoundhand, 261 | (_, false, Some(true)) if is_latin(c) | is_greek(c) => MathStyle::Italic, 262 | (_, false, None) if is_latin(c) | is_lower_greek(c) => MathStyle::Italic, 263 | (_, true, Some(false)) if is_latin(c) | is_greek(c) => MathStyle::Bold, 264 | (_, true, Some(true)) if is_latin(c) | is_greek(c) => MathStyle::BoldItalic, 265 | (_, true, None) if is_latin(c) | is_lower_greek(c) => MathStyle::BoldItalic, 266 | (_, true, None) if is_upper_greek(c) => MathStyle::Bold, 267 | (_, true, _) if is_digit(c) | matches!(c, 'Ϝ' | 'ϝ') => MathStyle::Bold, 268 | (_, _, Some(true) | None) if matches!(c, 'ı' | 'ȷ' | 'ħ') => { 269 | MathStyle::Italic 270 | } 271 | (_, _, Some(true) | None) if is_hebrew(c) => MathStyle::Hebrew, 272 | _ => MathStyle::Plain, 273 | } 274 | } 275 | } 276 | 277 | /// Returns an iterator that yields the styled equivalent of a `char`. 278 | /// 279 | /// This `struct` is created by the [`to_style`] function. See its 280 | /// documentation for more. 281 | #[derive(Debug, Clone)] 282 | pub struct ToStyle(core::array::IntoIter); 283 | 284 | impl ToStyle { 285 | #[inline] 286 | fn new(chars: [char; 2]) -> ToStyle { 287 | let mut iter = chars.into_iter(); 288 | if chars[1] == '\0' { 289 | iter.next_back(); 290 | } 291 | ToStyle(iter) 292 | } 293 | } 294 | 295 | impl Iterator for ToStyle { 296 | type Item = char; 297 | 298 | fn next(&mut self) -> Option { 299 | self.0.next() 300 | } 301 | 302 | fn size_hint(&self) -> (usize, Option) { 303 | self.0.size_hint() 304 | } 305 | 306 | fn fold(self, init: Acc, fold: Fold) -> Acc 307 | where 308 | Fold: FnMut(Acc, Self::Item) -> Acc, 309 | { 310 | self.0.fold(init, fold) 311 | } 312 | 313 | fn count(self) -> usize { 314 | self.0.count() 315 | } 316 | 317 | fn last(self) -> Option { 318 | self.0.last() 319 | } 320 | } 321 | 322 | impl DoubleEndedIterator for ToStyle { 323 | fn next_back(&mut self) -> Option { 324 | self.0.next_back() 325 | } 326 | 327 | fn rfold(self, init: Acc, rfold: Fold) -> Acc 328 | where 329 | Fold: FnMut(Acc, Self::Item) -> Acc, 330 | { 331 | self.0.rfold(init, rfold) 332 | } 333 | } 334 | 335 | impl ExactSizeIterator for ToStyle { 336 | fn len(&self) -> usize { 337 | self.0.len() 338 | } 339 | } 340 | 341 | impl FusedIterator for ToStyle {} 342 | 343 | impl fmt::Display for ToStyle { 344 | #[inline] 345 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 346 | for c in self.0.clone() { 347 | f.write_char(c)?; 348 | } 349 | Ok(()) 350 | } 351 | } 352 | 353 | /// Returns an iterator that yields the styled conversion of a `char`, as 354 | /// specified by `style`, as one or more `char`s. 355 | /// 356 | /// # Examples 357 | /// 358 | /// ``` 359 | /// use codex::styling::{to_style, MathStyle}; 360 | /// 361 | /// assert_eq!("𞺚", to_style('ظ', MathStyle::Looped).to_string()); 362 | /// assert_eq!("𝒬\u{fe00}", to_style('Q', MathStyle::Chancery).to_string()); 363 | /// 364 | /// let s = "xγΩAذح1∑س" 365 | /// .chars() 366 | /// .flat_map(|c| to_style(c, MathStyle::DoubleStruck)) 367 | /// .collect::(); 368 | /// assert_eq!("𝕩ℽΩ𝔸𞺸𞺧𝟙⅀𞺮", s); 369 | /// ``` 370 | pub fn to_style(c: char, style: MathStyle) -> ToStyle { 371 | use conversions::*; 372 | use MathStyle::*; 373 | let styled = match style { 374 | Plain => [c, '\0'], 375 | Bold => [to_bold(c), '\0'], 376 | Italic => [to_italic(c), '\0'], 377 | BoldItalic => [to_bold_italic(c), '\0'], 378 | Script => [to_script(c), '\0'], 379 | BoldScript => [to_bold_script(c), '\0'], 380 | Fraktur => [to_fraktur(c), '\0'], 381 | BoldFraktur => [to_bold_fraktur(c), '\0'], 382 | SansSerif => [to_sans_serif(c), '\0'], 383 | SansSerifBold => [to_sans_serif_bold(c), '\0'], 384 | SansSerifItalic => [to_sans_serif_italic(c), '\0'], 385 | SansSerifBoldItalic => [to_sans_serif_bold_italic(c), '\0'], 386 | Monospace => [to_monospace(c), '\0'], 387 | Isolated => [to_isolated(c), '\0'], 388 | Initial => [to_initial(c), '\0'], 389 | Tailed => [to_tailed(c), '\0'], 390 | Stretched => [to_stretched(c), '\0'], 391 | Looped => [to_looped(c), '\0'], 392 | DoubleStruck => [to_double_struck(c), '\0'], 393 | DoubleStruckItalic => [to_double_struck_italic(c), '\0'], 394 | Chancery => to_chancery(c), 395 | BoldChancery => to_bold_chancery(c), 396 | Roundhand => to_roundhand(c), 397 | BoldRoundhand => to_bold_roundhand(c), 398 | Hebrew => [to_hebrew(c), '\0'], 399 | }; 400 | ToStyle::new(styled) 401 | } 402 | 403 | /// Functions which convert a `char` to its specified styled form. 404 | /// 405 | /// Sourced from: 406 | /// - [Unicode Core Specification - Section 22.2, Letterlike Symbols] 407 | /// - [Letterlike Symbols] 408 | /// - [Mathematical Alphanumeric Symbols] 409 | /// - [Arabic Mathematical Alphabetic Symbols] 410 | /// 411 | /// [Unicode Core Specification - Section 22.2, Letterlike Symbols]: 412 | /// [Letterlike Symbols]: 413 | /// [Mathematical Alphanumeric Symbols]: 414 | /// [Arabic Mathematical Alphabetic Symbols]: 415 | mod conversions { 416 | const VARIATION_SELECTOR_1: char = '\u{FE00}'; 417 | const VARIATION_SELECTOR_2: char = '\u{FE01}'; 418 | 419 | #[inline] 420 | pub fn is_digit(c: char) -> bool { 421 | c.is_ascii_digit() 422 | } 423 | 424 | #[inline] 425 | pub fn is_latin(c: char) -> bool { 426 | c.is_ascii_alphabetic() 427 | } 428 | 429 | #[inline] 430 | pub fn is_greek(c: char) -> bool { 431 | is_upper_greek(c) || is_lower_greek(c) 432 | } 433 | 434 | #[inline] 435 | pub fn is_upper_greek(c: char) -> bool { 436 | matches!(c, 'Α'..='Ω' | '∇' | 'ϴ') 437 | } 438 | 439 | #[inline] 440 | pub fn is_lower_greek(c: char) -> bool { 441 | matches!(c, 'α'..='ω' | '∂' | 'ϵ' | 'ϑ' | 'ϰ' | 'ϕ' | 'ϱ' | 'ϖ') 442 | } 443 | 444 | #[inline] 445 | pub fn is_hebrew(c: char) -> bool { 446 | matches!(c, 'א'..='ד') 447 | } 448 | 449 | /// The character given by adding `delta` to the codepoint of `c`. 450 | #[inline] 451 | fn apply_delta(c: char, delta: u32) -> char { 452 | std::char::from_u32((c as u32) + delta).unwrap() 453 | } 454 | 455 | pub fn to_bold(c: char) -> char { 456 | let delta = match c { 457 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 458 | // Bold symbols (U+1D400..U+1D433) 459 | 'A'..='Z' => 0x1D3BF, 460 | 'a'..='z' => 0x1D3B9, 461 | // Bold Greek symbols (U+1D6A8..U+1D6DA) 462 | 'Α'..='Ρ' => 0x1D317, 463 | 'ϴ' => 0x1D2C5, 464 | 'Σ'..='Ω' => 0x1D317, 465 | '∇' => 0x1B4BA, 466 | 'α'..='ω' => 0x1D311, 467 | // Additional bold Greek symbols (U+1D6DB..U+1D6E1) 468 | '∂' => 0x1B4D9, 469 | 'ϵ' => 0x1D2E7, 470 | 'ϑ' => 0x1D30C, 471 | 'ϰ' => 0x1D2EE, 472 | 'ϕ' => 0x1D30A, 473 | 'ϱ' => 0x1D2EF, 474 | 'ϖ' => 0x1D30B, 475 | // Additional bold Greek symbols (U+1D7CA..U+1D7CB) 476 | 'Ϝ'..='ϝ' => 0x1D3EE, 477 | // Bold digits (U+1D7CE..U+1D7D7) 478 | '0'..='9' => 0x1D79E, 479 | _ => return c, 480 | }; 481 | apply_delta(c, delta) 482 | } 483 | 484 | pub fn to_italic(c: char) -> char { 485 | let delta = match c { 486 | // Letterlike Symbols Block (U+2100..U+214F) 487 | // Letterlike symbols (U+2100..U+2134) 488 | 'h' => 0x20A6, 489 | 'ħ' => 0x1FE8, 490 | 491 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 492 | // Italic symbols (U+1D434..U+1D467) 493 | 'A'..='Z' => 0x1D3F3, 494 | 'a'..='z' => 0x1D3ED, 495 | // Dotless symbols (U+1D6A4..U+1D6A5) 496 | 'ı' => 0x1D573, 497 | 'ȷ' => 0x1D46E, 498 | // Italic Greek symbols (U+1D6E2..U+1D714) 499 | 'Α'..='Ρ' => 0x1D351, 500 | 'ϴ' => 0x1D2FF, 501 | 'Σ'..='Ω' => 0x1D351, 502 | '∇' => 0x1B4F4, 503 | 'α'..='ω' => 0x1D34B, 504 | // Additional italic Greek symbols (U+1D715..U+1D71B) 505 | '∂' => 0x1B513, 506 | 'ϵ' => 0x1D321, 507 | 'ϑ' => 0x1D346, 508 | 'ϰ' => 0x1D328, 509 | 'ϕ' => 0x1D344, 510 | 'ϱ' => 0x1D329, 511 | 'ϖ' => 0x1D345, 512 | _ => return c, 513 | }; 514 | apply_delta(c, delta) 515 | } 516 | 517 | pub fn to_bold_italic(c: char) -> char { 518 | let delta = match c { 519 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 520 | // Bold italic symbols (U+1D468..U+1D49B) 521 | 'A'..='Z' => 0x1D427, 522 | 'a'..='z' => 0x1D421, 523 | // Bold italic Greek symbols (U+1D71C..U+1D74E) 524 | 'Α'..='Ρ' => 0x1D38B, 525 | 'ϴ' => 0x1D339, 526 | 'Σ'..='Ω' => 0x1D38B, 527 | '∇' => 0x1B52E, 528 | 'α'..='ω' => 0x1D385, 529 | // Additional bold italic Greek symbols (U+1D74F..U+1D755) 530 | '∂' => 0x1B54D, 531 | 'ϵ' => 0x1D35B, 532 | 'ϑ' => 0x1D380, 533 | 'ϰ' => 0x1D362, 534 | 'ϕ' => 0x1D37E, 535 | 'ϱ' => 0x1D363, 536 | 'ϖ' => 0x1D37F, 537 | _ => return c, 538 | }; 539 | apply_delta(c, delta) 540 | } 541 | 542 | pub fn to_script(c: char) -> char { 543 | let delta = match c { 544 | // Letterlike Symbols Block (U+2100..U+214F) 545 | // Letterlike symbols (U+2100..U+2134) 546 | 'g' => 0x20A3, 547 | 'H' => 0x20C3, 548 | 'I' => 0x20C7, 549 | 'L' => 0x20C6, 550 | 'R' => 0x20C9, 551 | 'B' => 0x20EA, 552 | 'e' => 0x20CA, 553 | 'E'..='F' => 0x20EB, 554 | 'M' => 0x20E6, 555 | 'o' => 0x20C5, 556 | 557 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 558 | // Script symbols (U+1D49C..U+1D4CF) 559 | 'A'..='Z' => 0x1D45B, 560 | 'a'..='z' => 0x1D455, 561 | _ => return c, 562 | }; 563 | apply_delta(c, delta) 564 | } 565 | 566 | pub fn to_bold_script(c: char) -> char { 567 | let delta = match c { 568 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 569 | // Bold script symbols (U+1D4D0..U+1D503) 570 | 'A'..='Z' => 0x1D48F, 571 | 'a'..='z' => 0x1D489, 572 | _ => return c, 573 | }; 574 | apply_delta(c, delta) 575 | } 576 | 577 | pub fn to_fraktur(c: char) -> char { 578 | let delta = match c { 579 | // Letterlike Symbols Block (U+2100..U+214F) 580 | // Letterlike symbols (U+2100..U+2134) 581 | 'H' => 0x20C4, 582 | 'I' => 0x20C8, 583 | 'R' => 0x20CA, 584 | 'Z' => 0x20CE, 585 | 'C' => 0x20EA, 586 | 587 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 588 | // Fraktur symbols (U+1D504..U+1D537) 589 | 'A'..='Z' => 0x1D4C3, 590 | 'a'..='z' => 0x1D4BD, 591 | _ => return c, 592 | }; 593 | apply_delta(c, delta) 594 | } 595 | 596 | pub fn to_bold_fraktur(c: char) -> char { 597 | let delta = match c { 598 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 599 | // Bold Fraktur symbols (U+1D56C..U+1D59F) 600 | 'A'..='Z' => 0x1D52B, 601 | 'a'..='z' => 0x1D525, 602 | _ => return c, 603 | }; 604 | apply_delta(c, delta) 605 | } 606 | 607 | pub fn to_sans_serif(c: char) -> char { 608 | let delta = match c { 609 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 610 | // Sans-serif symbols (U+1D5A0..U+1D5D3) 611 | 'A'..='Z' => 0x1D55F, 612 | 'a'..='z' => 0x1D559, 613 | // Sans-serif digits (U+1D7E2..U+1D7EB) 614 | '0'..='9' => 0x1D7B2, 615 | _ => return c, 616 | }; 617 | apply_delta(c, delta) 618 | } 619 | 620 | pub fn to_sans_serif_bold(c: char) -> char { 621 | let delta = match c { 622 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 623 | // Sans-serif bold symbols (U+1D5D4..U+1D607) 624 | 'A'..='Z' => 0x1D593, 625 | 'a'..='z' => 0x1D58D, 626 | // Sans-serif bold Greek symbols (U+1D756..U+1D788) 627 | 'Α'..='Ρ' => 0x1D3C5, 628 | 'ϴ' => 0x1D373, 629 | 'Σ'..='Ω' => 0x1D3C5, 630 | '∇' => 0x1B568, 631 | 'α'..='ω' => 0x1D3BF, 632 | // Additional sans-serif bold Greek symbols (U+1D789..U+1D78F) 633 | '∂' => 0x1B587, 634 | 'ϵ' => 0x1D395, 635 | 'ϑ' => 0x1D3BA, 636 | 'ϰ' => 0x1D39C, 637 | 'ϕ' => 0x1D3B8, 638 | 'ϱ' => 0x1D39D, 639 | 'ϖ' => 0x1D3B9, 640 | // Sans-serif bold digits (U+1D7EC..U+1D7F5) 641 | '0'..='9' => 0x1D7BC, 642 | _ => return c, 643 | }; 644 | apply_delta(c, delta) 645 | } 646 | 647 | pub fn to_sans_serif_italic(c: char) -> char { 648 | let delta = match c { 649 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 650 | // Sans-serif italic symbols (U+1D608..U+1D63B) 651 | 'A'..='Z' => 0x1D5C7, 652 | 'a'..='z' => 0x1D5C1, 653 | _ => return c, 654 | }; 655 | apply_delta(c, delta) 656 | } 657 | 658 | pub fn to_sans_serif_bold_italic(c: char) -> char { 659 | let delta = match c { 660 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 661 | // Sans-serif bold italic symbols (U+1D63C..U+1D66F) 662 | 'A'..='Z' => 0x1D5FB, 663 | 'a'..='z' => 0x1D5F5, 664 | // Sans-serif bold italic Greek symbols (U+1D790..U+1D7C2) 665 | 'Α'..='Ρ' => 0x1D3FF, 666 | 'ϴ' => 0x1D3AD, 667 | 'Σ'..='Ω' => 0x1D3FF, 668 | '∇' => 0x1B5A2, 669 | 'α'..='ω' => 0x1D3F9, 670 | // Additional sans-serif bold italic Greek symbols (U+1D7C3..U+1D7C9) 671 | '∂' => 0x1B5C1, 672 | 'ϵ' => 0x1D3CF, 673 | 'ϑ' => 0x1D3F4, 674 | 'ϰ' => 0x1D3D6, 675 | 'ϕ' => 0x1D3F2, 676 | 'ϱ' => 0x1D3D7, 677 | 'ϖ' => 0x1D3F3, 678 | _ => return c, 679 | }; 680 | apply_delta(c, delta) 681 | } 682 | 683 | pub fn to_monospace(c: char) -> char { 684 | let delta = match c { 685 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 686 | // Monospace symbols (U+1D670..U+1D6A3) 687 | 'A'..='Z' => 0x1D62F, 688 | 'a'..='z' => 0x1D629, 689 | // Monospace digits (U+1D7F6..U+1D7FF) 690 | '0'..='9' => 0x1D7C6, 691 | _ => return c, 692 | }; 693 | apply_delta(c, delta) 694 | } 695 | 696 | pub fn to_isolated(c: char) -> char { 697 | let delta = match c { 698 | // Arabic Mathematical Alphabetic Symbols Block (U+1EE00..U+1EEFF) 699 | // Isolated symbols (U+1EE00..U+1EE1F) 700 | 'ا'..='ب' => 0x1E7D9, 701 | 'ج' => 0x1E7D6, 702 | 'د' => 0x1E7D4, 703 | 'و' => 0x1E7BD, 704 | 'ز' => 0x1E7D4, 705 | 'ح' => 0x1E7DA, 706 | 'ط' => 0x1E7D1, 707 | 'ي' => 0x1E7BF, 708 | 'ك'..='ن' => 0x1E7C7, 709 | 'س' => 0x1E7DB, 710 | 'ع' => 0x1E7D6, 711 | 'ف' => 0x1E7CF, 712 | 'ص' => 0x1E7DC, 713 | 'ق' => 0x1E7D0, 714 | 'ر' => 0x1E7E2, 715 | 'ش' => 0x1E7E0, 716 | 'ت'..='ث' => 0x1E7EB, 717 | 'خ' => 0x1E7E9, 718 | 'ذ' => 0x1E7E8, 719 | 'ض' => 0x1E7E3, 720 | 'ظ' => 0x1E7E2, 721 | 'غ' => 0x1E7E1, 722 | 'ٮ' => 0x1E7AE, 723 | 'ں' => 0x1E763, 724 | 'ڡ' => 0x1E77D, 725 | 'ٯ' => 0x1E7B0, 726 | _ => return c, 727 | }; 728 | apply_delta(c, delta) 729 | } 730 | 731 | pub fn to_initial(c: char) -> char { 732 | let delta = match c { 733 | // Arabic Mathematical Alphabetic Symbols Block (U+1EE00..U+1EEFF) 734 | // Initial symbols (U+1EE21..U+1EE3B) 735 | 'ب' => 0x1E7F9, 736 | 'ج' => 0x1E7F6, 737 | 'ه' => 0x1E7DD, 738 | 'ح' => 0x1E7FA, 739 | 'ي' => 0x1E7DF, 740 | 'ك'..='ن' => 0x1E7E7, 741 | 'س' => 0x1E7FB, 742 | 'ع' => 0x1E7F6, 743 | 'ف' => 0x1E7EF, 744 | 'ص' => 0x1E7FC, 745 | 'ق' => 0x1E7F0, 746 | 'ش' => 0x1E800, 747 | 'ت'..='ث' => 0x1E80B, 748 | 'خ' => 0x1E809, 749 | 'ض' => 0x1E803, 750 | 'غ' => 0x1E801, 751 | _ => return c, 752 | }; 753 | apply_delta(c, delta) 754 | } 755 | 756 | pub fn to_tailed(c: char) -> char { 757 | let delta = match c { 758 | // Arabic Mathematical Alphabetic Symbols Block (U+1EE00..U+1EEFF) 759 | // Tailed symbols (U+1EE42..U+1EE5F) 760 | 'ج' => 0x1E816, 761 | 'ح' => 0x1E81A, 762 | 'ي' => 0x1E7FF, 763 | 'ل' => 0x1E807, 764 | 'ن' => 0x1E807, 765 | 'س' => 0x1E81B, 766 | 'ع' => 0x1E816, 767 | 'ص' => 0x1E81C, 768 | 'ق' => 0x1E810, 769 | 'ش' => 0x1E820, 770 | 'خ' => 0x1E829, 771 | 'ض' => 0x1E823, 772 | 'غ' => 0x1E821, 773 | 'ں' => 0x1E7A3, 774 | 'ٯ' => 0x1E7F0, 775 | _ => return c, 776 | }; 777 | apply_delta(c, delta) 778 | } 779 | 780 | pub fn to_stretched(c: char) -> char { 781 | let delta = match c { 782 | // Arabic Mathematical Alphabetic Symbols Block (U+1EE00..U+1EEFF) 783 | // Stretched symbols (U+1EE61..U+1EE7E) 784 | 'ب' => 0x1E839, 785 | 'ج' => 0x1E836, 786 | 'ه' => 0x1E81D, 787 | 'ح' => 0x1E83A, 788 | 'ط' => 0x1E831, 789 | 'ي' => 0x1E81F, 790 | 'ك' => 0x1E827, 791 | 'م'..='ن' => 0x1E827, 792 | 'س' => 0x1E83B, 793 | 'ع' => 0x1E836, 794 | 'ف' => 0x1E82F, 795 | 'ص' => 0x1E83C, 796 | 'ق' => 0x1E830, 797 | 'ش' => 0x1E840, 798 | 'ت'..='ث' => 0x1E84B, 799 | 'خ' => 0x1E849, 800 | 'ض' => 0x1E843, 801 | 'ظ' => 0x1E842, 802 | 'غ' => 0x1E841, 803 | 'ٮ' => 0x1E80E, 804 | 'ڡ' => 0x1E7DD, 805 | _ => return c, 806 | }; 807 | apply_delta(c, delta) 808 | } 809 | 810 | pub fn to_looped(c: char) -> char { 811 | let delta = match c { 812 | // Arabic Mathematical Alphabetic Symbols Block (U+1EE00..U+1EEFF) 813 | // Looped symbols (U+1EE80..U+1EE9B) 814 | 'ا'..='ب' => 0x1E859, 815 | 'ج' => 0x1E856, 816 | 'د' => 0x1E854, 817 | 'ه'..'و' => 0x1E83D, 818 | 'ز' => 0x1E854, 819 | 'ح' => 0x1E85A, 820 | 'ط' => 0x1E851, 821 | 'ي' => 0x1E83F, 822 | 'ل'..='ن' => 0x1E847, 823 | 'س' => 0x1E85B, 824 | 'ع' => 0x1E856, 825 | 'ف' => 0x1E84F, 826 | 'ص' => 0x1E85C, 827 | 'ق' => 0x1E850, 828 | 'ر' => 0x1E862, 829 | 'ش' => 0x1E860, 830 | 'ت'..='ث' => 0x1E86B, 831 | 'خ' => 0x1E869, 832 | 'ذ' => 0x1E868, 833 | 'ض' => 0x1E863, 834 | 'ظ' => 0x1E862, 835 | 'غ' => 0x1E861, 836 | _ => return c, 837 | }; 838 | apply_delta(c, delta) 839 | } 840 | 841 | pub fn to_double_struck(c: char) -> char { 842 | let delta = match c { 843 | // Letterlike Symbols Block (U+2100..U+214F) 844 | // Letterlike symbols (U+2100..U+2134) 845 | 'C' => 0x20BF, 846 | 'H' => 0x20C5, 847 | 'N' => 0x20C7, 848 | 'P'..='Q' => 0x20C9, 849 | 'R' => 0x20CB, 850 | 'Z' => 0x20CA, 851 | // Additional letterlike symbols (U+2139..U+213F) 852 | 'π' => 0x1D7C, 853 | 'γ' => 0x1D8A, 854 | 'Γ' => 0x1DAB, 855 | 'Π' => 0x1D9F, 856 | // Double-struck large operator (U+2140) 857 | '∑' => return '⅀', // delta is negative 858 | 859 | // Mathematical Alphanumeric Symbols Block (U+1D400..U+1D7FF) 860 | // Double-struck symbols (U+1D538..U+1D56B) 861 | 'A'..='Z' => 0x1D4F7, 862 | 'a'..='z' => 0x1D4F1, 863 | // Double-struck digits (U+1D7D8..U+1D7E1) 864 | '0'..='9' => 0x1D7A8, 865 | 866 | // Arabic Mathematical Alphabetic Symbols Block (U+1EE00..U+1EEFF) 867 | // Double-struck symbols (U+1EEA1..U+1EEBB) 868 | 'ب' => 0x1E879, 869 | 'ج' => 0x1E876, 870 | 'د' => 0x1E874, 871 | 'و' => 0x1E85D, 872 | 'ز' => 0x1E874, 873 | 'ح' => 0x1E87A, 874 | 'ط' => 0x1E871, 875 | 'ي' => 0x1E85F, 876 | 'ل'..='ن' => 0x1E867, 877 | 'س' => 0x1E87B, 878 | 'ع' => 0x1E876, 879 | 'ف' => 0x1E86F, 880 | 'ص' => 0x1E87C, 881 | 'ق' => 0x1E870, 882 | 'ر' => 0x1E882, 883 | 'ش' => 0x1E880, 884 | 'ت'..='ث' => 0x1E88B, 885 | 'خ' => 0x1E889, 886 | 'ذ' => 0x1E888, 887 | 'ض' => 0x1E883, 888 | 'ظ' => 0x1E882, 889 | 'غ' => 0x1E881, 890 | _ => return c, 891 | }; 892 | apply_delta(c, delta) 893 | } 894 | 895 | pub fn to_double_struck_italic(c: char) -> char { 896 | let delta = match c { 897 | // Letterlike Symbols Block (U+2100..U+214F) 898 | // Double-struck italic math symbols (U+2145..U+2149) 899 | 'D' => 0x2101, 900 | 'd'..='e' => 0x20E2, 901 | 'i'..='j' => 0x20DF, 902 | _ => return c, 903 | }; 904 | apply_delta(c, delta) 905 | } 906 | 907 | pub fn to_chancery(c: char) -> [char; 2] { 908 | // Standardized Variation Sequences (uppercase Latin script characters) 909 | // Variation Sequences (lowercase Latin script characters) 910 | let next = if is_latin(c) { VARIATION_SELECTOR_1 } else { '\0' }; 911 | [to_script(c), next] 912 | } 913 | 914 | pub fn to_bold_chancery(c: char) -> [char; 2] { 915 | // Variation Sequences (Latin script characters) 916 | let next = if is_latin(c) { VARIATION_SELECTOR_1 } else { '\0' }; 917 | [to_bold_script(c), next] 918 | } 919 | 920 | pub fn to_roundhand(c: char) -> [char; 2] { 921 | // Standardized Variation Sequences (uppercase Latin script characters) 922 | // Variation Sequences (lowercase Latin script characters) 923 | let next = if is_latin(c) { VARIATION_SELECTOR_2 } else { '\0' }; 924 | [to_script(c), next] 925 | } 926 | 927 | pub fn to_bold_roundhand(c: char) -> [char; 2] { 928 | // Variation Sequences (Latin script characters) 929 | let next = if is_latin(c) { VARIATION_SELECTOR_2 } else { '\0' }; 930 | [to_bold_script(c), next] 931 | } 932 | 933 | pub fn to_hebrew(c: char) -> char { 934 | let delta = match c { 935 | // Letterlike Symbols Block (U+2100..U+214F) 936 | // Hebrew letterlike math symbols (U+2135..U+2138) 937 | 'א'..='ד' => 0x1B65, 938 | _ => return c, 939 | }; 940 | apply_delta(c, delta) 941 | } 942 | } 943 | --------------------------------------------------------------------------------