diff --git a/AGENTS.md b/AGENTS.md index c6ca9e756..7093cf698 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled (always the minimized tree). Shared zipper: `src/rules_archive.rs`, invoked as `cargo run --bin package-rules -- Rules [--minimize]`. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style rewrite of all YAML: comments dropped). Each language/braille subdir is stored as `/.zip`; top-level and `Intent/` YAML stay loose. Inner language zips use BZIP2; the outer downloadable archive uses DEFLATE so `unzip` works. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (comments stripped from `Languages/**/unicode.yaml` and `unicode-full.yaml` only). Each language/braille subdir is stored as `/.zip`; top-level and `Intent/` YAML stay loose. Inner language zips use BZIP2; the outer downloadable archive uses DEFLATE so `unzip` works. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. ## Translation Conventions - `t:` means untranslated or unverified. @@ -33,6 +33,7 @@ but add common mistakes of AI agents here instead. - Do not mirror README content here; keep guidance agent-specific. - Avoid broad formatting sweeps; do not run `cargo fmt` in this repo. - Keep code/rule changes focused and validate with targeted tests first: `cargo test ` +- Do **not** change test goldens (expected braille/speech/nav strings) unless the user explicitly says to. Prefer fixing code/rules to match existing goldens; if a golden looks wrong vs the spec, ask before editing it. - do not do any git commands unless explicitly asked for - Rust coverage is in `target/coverage/`. - When working with GitHub, e.g. looking at PRs and issues, check if the GitHub CLI is installed (`gh --version`). diff --git a/Cargo.toml b/Cargo.toml index fcf5428d9..2e73cbf80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mathcat" -version = "0.7.7-alpha.1" +version = "0.7.6-rc.4" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" diff --git a/PythonScripts/audit_translations/README.md b/PythonScripts/audit_translations/README.md index fd399e412..8aedf1265 100644 --- a/PythonScripts/audit_translations/README.md +++ b/PythonScripts/audit_translations/README.md @@ -77,7 +77,6 @@ uv run --project PythonScripts audit-translations --list * Region variants are shown as `lang-region` (e.g., `zz-aa`) based on subdirectories under `Rules/Languages/`. * `--source`: Sets the source/reference language. Defaults to `en`. * `--file`: Audits a single specific file instead of the whole directory. -* `--exclude`: Exclude one or more files from the audit. * `--rules-dir`: Override the Rules/Languages directory path. * `--only`: Filter issue types (comma-separated): `missing`, `untranslated`, `extra`, `diffs`, `all`. * `--verbose`: Show detailed output including source/target snippets for rule differences. @@ -101,17 +100,9 @@ uv run audit-translations de # Compare Norwegian Bokmal against Swedish instead of English uv run audit-translations nb --source sv -# Audit only a specific file (note: --file is incompatible with --exclude) -uv run audit-translations es --file ClearSpeak_Rules.yaml +# Audit only a specific file uv run audit-translations es --file SharedRules/default.yaml -# Exclude a list of files from the audit, note that if you use this option before specifying the -# target language you'll need to use the option terminator (--). -# (note: --exclude is incompatible with --file) -uv run audit-translations es --exclude unicode-full.yaml -uv run audit-translations es --exclude unicode-full.yaml unicode.yaml -uv run audit-translations --exclude unicode-full.yaml -- es - # Audit a regional variant (merges Rules/Languages/de and Rules/Languages/de/CH) uv run audit-translations de-CH diff --git a/PythonScripts/audit_translations/auditor.py b/PythonScripts/audit_translations/auditor.py index 37ec44f13..b67cbf911 100644 --- a/PythonScripts/audit_translations/auditor.py +++ b/PythonScripts/audit_translations/auditor.py @@ -46,31 +46,24 @@ def is_definitions_file(file_path: str | Path) -> bool: return Path(file_path).name == "definitions.yaml" -def get_yaml_files(lang_dir: Path, region_dir: Path | None = None, excluded_files: list[str] | None = None) -> list[Path]: +def get_yaml_files(lang_dir: Path, region_dir: Path | None = None) -> list[Path]: """Get all YAML files to audit for a language, including region overrides.""" files: set[Path] = set() - def collect_from(directory: Path, root: Path, excluded: list[str] | None) -> None: + def collect_from(directory: Path, root: Path) -> None: if not directory.exists(): return - - excluded_paths = {Path(path) for path in excluded or []} - - candidates = {f for f in directory.glob("*.yaml") if f.name != "prefs.yaml"} - candidates.update((directory / "SharedRules").glob("*.yaml")) - - relative_candidates = {f.relative_to(directory): f for f in candidates} - - for path in excluded_paths - relative_candidates.keys(): - console.print(f"\n[yellow]⚠ Warning:[/] File to exclude {path} does not exist.") - - files.update( - f.relative_to(root) for relative_path, f in relative_candidates.items() if relative_path not in excluded_paths - ) - - collect_from(lang_dir, lang_dir, excluded_files) + for f in directory.glob("*.yaml"): + if f.name != "prefs.yaml": + files.add(f.relative_to(root)) + shared_dir = directory / "SharedRules" + if shared_dir.exists(): + for f in shared_dir.glob("*.yaml"): + files.add(f.relative_to(root)) + + collect_from(lang_dir, lang_dir) if region_dir: - collect_from(region_dir, region_dir, excluded_files) + collect_from(region_dir, region_dir) return sorted(files) @@ -222,7 +215,6 @@ def merge_definitions( def audit_language( language: str, specific_file: str | None = None, - excluded_files: list[str] | None = None, rules_dir: str | None = None, issue_filter: set[str] | None = None, verbose: bool = False, @@ -256,7 +248,7 @@ def audit_language( raise AuditError(f"Target region directory not found: {translated_region_dir}") # Get list of files to audit - files = [specific_file] if specific_file else get_yaml_files(source_dir, source_region_dir, excluded_files) + files = [specific_file] if specific_file else get_yaml_files(source_dir, source_region_dir) print_audit_header(language, len(files), source_language) diff --git a/PythonScripts/audit_translations/cli.py b/PythonScripts/audit_translations/cli.py index 5bdf30e6d..6916a636e 100644 --- a/PythonScripts/audit_translations/cli.py +++ b/PythonScripts/audit_translations/cli.py @@ -30,18 +30,7 @@ def main() -> None: parser.add_argument("language", nargs="?", help="Language code to audit (e.g., 'es', 'de', 'fi')") parser.add_argument("--source", default="en", help="Source/reference language code (default: 'en')") - file_group = parser.add_mutually_exclusive_group() - file_group.add_argument( - "--file", - dest="specific_file", - help="Audit only a specific file (e.g., 'SharedRules/default.yaml')", - ) - file_group.add_argument( - "--exclude", - nargs="+", - dest="excluded_files", - help="Exclude a list of files from the audit.", - ) + parser.add_argument("--file", dest="specific_file", help="Audit only a specific file (e.g., 'SharedRules/default.yaml')") parser.add_argument("--list", action="store_true", help="List available languages") parser.add_argument("--rules-dir", help="Override Rules/Languages directory path") parser.add_argument( @@ -78,7 +67,6 @@ def main() -> None: audit_language( args.language, args.specific_file, - args.excluded_files, args.rules_dir, issue_filter, args.verbose, diff --git a/PythonScripts/audit_translations/tests/test_cli_end_to_end.py b/PythonScripts/audit_translations/tests/test_cli_end_to_end.py index 40cf3f8b2..7876bc0de 100644 --- a/PythonScripts/audit_translations/tests/test_cli_end_to_end.py +++ b/PythonScripts/audit_translations/tests/test_cli_end_to_end.py @@ -127,80 +127,6 @@ def test_cli_main_rich_only_filters_issue_groups(capsys, monkeypatch) -> None: assert "Structure Differences" not in output -@pytest.mark.parametrize( - ("excluded_file", "expected_file", "expected_rule", "unexpected_rule"), - [ - ( - "default.yaml", - "SharedRules/default.yaml", - "shared-only", - "root-only", - ), - ( - "SharedRules/default.yaml", - "default.yaml", - "root-only", - "shared-only", - ), - ], - ids=["exclude-root", "exclude-shared-rules"], -) -def test_cli_main_exclude_uses_relative_paths( - tmp_path, - capsys, - monkeypatch, - excluded_file, - expected_file, - expected_rule, - unexpected_rule, -) -> None: - """Ensure --exclude distinguishes root files from SharedRules files.""" - rules_dir = tmp_path / "Rules" / "Languages" - source_dir = rules_dir / "en" - target_dir = rules_dir / "de" - - (source_dir / "SharedRules").mkdir(parents=True) - (target_dir / "SharedRules").mkdir(parents=True) - - (source_dir / "default.yaml").write_text( - '- name: root-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "root"\n', - encoding="utf-8", - ) - (source_dir / "SharedRules" / "default.yaml").write_text( - '- name: shared-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "shared"\n', - encoding="utf-8", - ) - - target_rule = '- name: target-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "target"\n' - (target_dir / "default.yaml").write_text( - target_rule, - encoding="utf-8", - ) - (target_dir / "SharedRules" / "default.yaml").write_text( - target_rule, - encoding="utf-8", - ) - - args = [ - "de", - "--rules-dir", - str(rules_dir), - "--exclude", - excluded_file, - "--only", - "missing", - ] - monkeypatch.setattr(sys, "argv", ["audit_translations", *args]) - - audit_cli.main() - output = strip_ansi(capsys.readouterr().out) - - assert "Files to check: 1" in output - assert expected_file in output - assert expected_rule in output - assert unexpected_rule not in output - - def test_cli_main_accepts_source_language(capsys, monkeypatch) -> None: """ Ensure --source changes the reference language without changing target semantics. diff --git a/Rules/Braille/Nemeth/unicode-full.yaml b/Rules/Braille/Nemeth/unicode-full.yaml index bf4f842ea..30ffc561d 100644 --- a/Rules/Braille/Nemeth/unicode-full.yaml +++ b/Rules/Braille/Nemeth/unicode-full.yaml @@ -1,1364 +1,1364 @@ ---- -# To greatly simplify typeface/language generation, the chars have unique ASCII chars for them: -# Typeface: S: sans-serif, B: bold, T: script/blackboard, I: italic, R: Roman -# Language: E: English, D: German, G: Greek, V: Greek variants, H: Hebrew, U: Russian -# Indicators: C: capital, L: letter, N: number, P: punctuation, M: multipurpose -# Others: W -- whitespace that should be kept (e.g, in a numeral) -# SRE doesn't have H: Hebrew, so not encoded (yet) -# Some of the weird letters (e.g., circled letters) I didn't translate with the above because -# they don't interact with rules since they are enclosed in other chars. - - - "⨯": [t: "⠈⠡"] # U+2A2F(VECTOR OR CROSS PRODUCT) -- make the same as 0x00D7 (Multiplication sign) - - # --- Nemeth Default Alphabet bold chars. --- - - "𝐀-𝐙": # 0x1d400 - 0x1d419 - - tc: "BE" - - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - "𝐚-𝐳": # 0x1d41a - 0x1d433 - - tc: "BE" - - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 - - tc: "BG" - - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝛂-𝛒": # 0x1d6c2 - 0x1d6d2 - - tc: "BG" - - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒', 'αβγδεζηθικλμνξοπρ')" - - - "𝛔-𝛖": # 0x1d6d4 - 0x1d6d6 - - tc: "BG" - - spell: "translate('.', '𝛔𝛕𝛖', 'στυ')" - - - "𝛘-𝛚": # 0x1d6d8 - 0x1d6da - - tc: "BG" - - spell: "translate('.', '𝛘𝛙𝛚', 'χψω')" - - - "𝛁": [tc: "BGL⠫"] # 0x1d6c1 - - "𝛓": [tc: "BVL⠎"] # 0x1d6d3 - - "𝛗": [tc: "BVL⠋"] # 0x1d6d7 - - "𝛛": [tc: "⠸⠈⠙"] # 0x1d6db - - "𝛜": [tc: "BGL⠑"] # 0x1d6dc - - "𝛝": [tc: "BVL⠹"] # 0x1d6dd - - "𝛞": [tc: "BGL⠅"] # 0x1d6de - - "𝛟": [tc: "BGL⠋"] # 0x1d6df - - "𝛠": [tc: "BGL⠗"] # 0x1d6e0 - - "𝛡": [tc: "BGL⠏"] # 0x1d6e1 - - - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 - - tc: "B" - - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" - - # --- Nemeth Default Alphabet bold-fraktur chars. --- - - - "𝕬-𝖅": # 0x1d56c - 0x1d585 - - tc: "BD" - - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝖆-𝖟": # 0x1d586 - 0x1d59f - - tc: "BD" - - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet bold-italic chars. --- - - "𝑨-𝒁": # 0x1d468 - 0x1d481 - - tc: "BE" - - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝒂-𝒛": # 0x1d482 - 0x1d49b - - tc: "BE" - - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝜜-𝜴": # 0x1d71c - 0x1d734 - - tc: "BG" - - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝜵": [tc: "BGL⠫"] # 0x1d735 - - - "𝜶-𝝆": # 0x1d736 - 0x1d746 - - tc: "BG" - - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆', 'αβγδεζηθικλμνξοπρ')" - - - "𝝇": [tc: "BVL⠎"] # 0x1d747 - - - "𝝈-𝝊": # 0x1d748 - 0x1d74a - - tc: "BG" - - spell: "translate('.', '𝝈𝝉𝝊', 'στυ')" - - - "𝝋": [tc: "BVL⠋"] # 0x1d74b - - - "𝝌-𝝎": # 0x1d74c - 0x1d74e - - tc: "BG" - - spell: "translate('.', '𝝌𝝍𝝎', 'χψω')" - - - "𝝏": [tc: "BVL⠙"] # 0x1d74f - - "𝝐": [tc: "BGL⠑"] # 0x1d750 - - "𝝑": [tc: "BVL⠹"] # 0x1d751 - - - "𝝒-𝝕": # 0x1d752 - 0x1d755 - - tc: "BG" - - spell: "translate('.', '𝝒𝝓𝝔𝝕', 'κφρπ')" - - # --- Nemeth Default Alphabet bold-script chars. --- - - - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 - - tc: "BTE" - - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝓪-𝔃": # 0x1d4ea - 0x1d503 - - tc: "BTE" - - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝚤-𝚥": # 0x1d6a4 - 0x1d6a5 - - tc: "" - - spell: "translate('.', '𝚤𝚥', '⠊⠚')" - -# Russian Alphabet (from Nemeth book) -# NOTE: is this translation needed? - - "А-Я": # 0x410 - 0x42f - - tc: "U" - - spell: "translate('.', 'АБВГДЕЗИЙКЛМНОПРСТУФЭ', 'ABVGDEZIYKLMNOPRSTUFE')" - - - "а-я": # 0x430 - 0x44f - - tc: "U" - - spell: "translate('.', 'абвгдезийклмнопрстуфэ', 'abvgdeziylkmnoprstufye')" - - - "①-⑨": # 0x2460 - 0x2473 - - tc: "⠫⠉⠸⠫⠼" - - spell: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" - - tc: "⠻" - - - "⓪": [tc: "⠫⠉⠸⠫⠼⠴⠻"] # 0x24ea - - - "⑩": [tc: "⠫⠉⠸⠫⠼⠂⠴⠻"] # 0x2469 - - "⑪": [tc: "⠫⠉⠸⠫⠼⠂⠂⠻"] # 0x246a - - "⑫": [tc: "⠫⠉⠸⠫⠼⠂⠆⠻"] # 0x246b - - "⑬": [tc: "⠫⠉⠸⠫⠼⠂⠒⠻"] # 0x246c - - "⑭": [tc: "⠫⠉⠸⠫⠼⠂⠲⠻"] # 0x246d - - "⑮": [tc: "⠫⠉⠸⠫⠼⠂⠢⠻"] # 0x246e - - "⑯": [tc: "⠫⠉⠸⠫⠼⠂⠖⠻"] # 0x246f - - "⑰": [tc: "⠫⠉⠸⠫⠼⠂⠶⠻"] # 0x2470 - - "⑱": [tc: "⠫⠉⠸⠫⠼⠂⠦⠻"] # 0x2471 - - "⑲": [tc: "⠫⠉⠸⠫⠼⠂⠔⠻"] # 0x2472 - - "⑳": [tc: "⠫⠉⠸⠫⠼⠆⠴⠻"] # 0x2473 - - - "㉑": [tc: "⠫⠉⠸⠫⠼⠆⠂⠻"] # 0x3251 - - "㉒": [tc: "⠫⠉⠸⠫⠼⠆⠆⠻"] # 0x3252 - - "㉓": [tc: "⠫⠉⠸⠫⠼⠆⠒⠻"] # 0x3253 - - "㉔": [tc: "⠫⠉⠸⠫⠼⠆⠲⠻"] # 0x3254 - - "㉕": [tc: "⠫⠉⠸⠫⠼⠆⠢⠻"] # 0x3255 - - "㉖": [tc: "⠫⠉⠸⠫⠼⠆⠖⠻"] # 0x3256 - - "㉗": [tc: "⠫⠉⠸⠫⠼⠆⠶⠻"] # 0x3257 - - "㉘": [tc: "⠫⠉⠸⠫⠼⠆⠦⠻"] # 0x3258 - - "㉙": [tc: "⠫⠉⠸⠫⠼⠆⠔⠻"] # 0x3259 - - "㉚": [tc: "⠫⠉⠸⠫⠼⠒⠴⠻"] # 0x325a - - "㉛": [tc: "⠫⠉⠸⠫⠼⠒⠂⠻"] # 0x325b - - "㉜": [tc: "⠫⠉⠸⠫⠼⠒⠆⠻"] # 0x325c - - "㉝": [tc: "⠫⠉⠸⠫⠼⠒⠒⠻"] # 0x325d - - "㉞": [tc: "⠫⠉⠸⠫⠼⠒⠲⠻"] # 0x325e - - "㉟": [tc: "⠫⠉⠸⠫⠼⠒⠢⠻"] # 0x325f - - "㊱": [tc: "⠫⠉⠸⠫⠼⠒⠖⠻"] # 0x32b1 - - "㊲": [tc: "⠫⠉⠸⠫⠼⠒⠶⠻"] # 0x32b2 - - "㊳": [tc: "⠫⠉⠸⠫⠼⠒⠦⠻"] # 0x32b3 - - "㊴": [tc: "⠫⠉⠸⠫⠼⠒⠔⠻"] # 0x32b4 - - "㊵": [tc: "⠫⠉⠸⠫⠼⠲⠴⠻"] # 0x32b5 - - "㊶": [tc: "⠫⠉⠸⠫⠼⠲⠂⠻"] # 0x32b6 - - "㊷": [tc: "⠫⠉⠸⠫⠼⠲⠆⠻"] # 0x32b7 - - "㊸": [tc: "⠫⠉⠸⠫⠼⠲⠒⠻"] # 0x32b8 - - "㊹": [tc: "⠫⠉⠸⠫⠼⠲⠲⠻"] # 0x32b9 - - "㊺": [tc: "⠫⠉⠸⠫⠼⠲⠢⠻"] # 0x32ba - - "㊻": [tc: "⠫⠉⠸⠫⠼⠲⠖⠻"] # 0x32bb - - "㊼": [tc: "⠫⠉⠸⠫⠼⠲⠶⠻"] # 0x32bc - - "㊽": [tc: "⠫⠉⠸⠫⠼⠲⠦⠻"] # 0x32bd - - "㊾": [tc: "⠫⠉⠸⠫⠼⠲⠔⠻"] # 0x32be - - "㊿": [tc: "⠫⠉⠸⠫⠼⠢⠴⠻"] # 0x32bf - -#NOTE: uncertainty here - - "Ⓐ-Ⓩ": # 0x24b6 - 0x24cf - - tc: "⠫⠉⠸⠫⠠" - - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠻" - - - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 - - tc: "⠫⠉⠸⠫" - - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" - - tc: "⠻" - - # --- Nemeth Default Alphabet circled-sans-serif chars. --- - - - "➀-➈": # 0x1f10b, 0x2780 - 0x2788 - - tc: "⠫⠉⠸⠫⠠⠨⠼" - - spell: "translate('.', '➀➁➂➃➄➅➆➇➈', '123456789')" - - tc: "⠻" - - - "🄋": [tc: "⠫⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10b - - "➉": [tc: "⠫⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2789 - - # --- Nemeth Default Alphabet comma chars. --- - - - "🄁-🄊": # 0x1f101 - 0x1f10a - - spell: "translate('.', '🄁🄂🄃🄄🄅🄆🄇🄈🄉🄊', '0123456789')" - - tc: "⠠" - - - # --- Nemeth Default Alphabet double-circled chars. --- - - - "⓵": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠻⠻"] # 0x24f5 - - "⓶": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠆⠻⠻"] # 0x24f6 - - "⓷": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠒⠻⠻"] # 0x24f7 - - "⓸": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠲⠻⠻"] # 0x24f8 - - "⓹": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠢⠻⠻"] # 0x24f9 - - "⓺": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠖⠻⠻"] # 0x24fa - - "⓻": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠶⠻⠻"] # 0x24fb - - "⓼": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠦⠻⠻"] # 0x24fc - - "⓽": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠔⠻⠻"] # 0x24fd - - "⓾": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x24fe - - # --- Nemeth Default Alphabet double-struck chars. --- - - "𝔸-𝕐": # 0x1d538 - 0x1d550 (partial, excludes ℂ ℍ ℕ ℙ ℚ ℝ ℤ) - - tc: "𝔹E" - - spell: "translate('.', '𝔸𝔹𝔻𝔼𝔽𝔾𝕀𝕁𝕂𝕃𝕄𝕆𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABDEFGIJKLMOSTUVWXY')" - - - "ℂ": [tc: "𝔹ECL⠉"] # 0x2102 - - "ℍ": [tc: "𝔹ECL⠓"] # 0x210d - - "ℕ": [tc: "𝔹ECL⠝"] # 0x2115 - - "ℙ": [tc: "𝔹ECL⠏"] # 0x2119 - - "ℚ": [tc: "𝔹ECL⠟"] # 0x211a - - "ℝ": [tc: "𝔹ECL⠗"] # 0x211d - - "ℤ": [tc: "𝔹ECL⠵"] # 0x2124 - - "Ω": [t: "CGL⠺"] # 0x2126 (Ohm sign (capital Greek omega)) - - - "𝕒-𝕫": # 0x1d552 - 0x1d56b - - tc: "𝔹E" - - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 - - tc: "𝔹" - - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" - - - # --- Nemeth Default Alphabet fraktur chars. --- - - - "𝔄-𝔜": # 0x1d504 - 0x1d51c - - tc: "D" - - spell: "translate('.', '𝔄𝔅𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABDEFGJKLMNOPQSTUVWXY')" - - - "ℭ": [tc: "DCL⠉"] # 0x212d - - "ℌ": [tc: "DCL⠓"] # 0x210c - - "ℑ": [tc: "DCL⠊"] # 0x2111 - - "ℜ": [tc: "DCL⠗"] # 0x211c - - "ℨ": [tc: "DCL⠵"] # 0x2128 - - - "𝔞-𝔷": # 0x1d51e - 0x1d537 - - tc: "D" - - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet fullwidth chars. --- - - - "A-Z": # 0xff21 - 0xff3a - - tc: "" - - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "a-z": # 0xff41 - 0xff5a - - tc: "" - - spell: "translate('.', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz')" - - - "0-9": # 0xff10 - 0xff19 - - tc: "" - - spell: "translate('.', '0123456789', '0123456789')" - - # --- Nemeth Default Alphabet italic chars -- treat as non-italic. --- - - - "𝐴-𝑍": # 0x1d434 - 0x1d44d - - tc: "E" - - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝑎-𝑧": # 0x1d44e - 0x1d467 - - tc: "E" - - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔ℎ𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa - - tc: "G" - - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝛻": [tc: "GL⠫"] # 0x1d6fb - - - "𝛼-𝜌": # 0x1d6fc - 0x1d70c - - tc: "G" - - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌', 'αβγδεζηθικλμνξοπρ')" - - "𝜍": [tc: "VL⠎"] # 0x1d70d - - - "𝜎": [tc: "GL⠎"] # 0x1d70e - - "𝜏": [tc: "GL⠞"] # 0x1d70f - - "𝜐": [tc: "GL⠥"] # 0x1d710 - - - "𝜑": [tc: "VL⠋"] # 0x1d711 - - - "𝜒": [tc: "GL⠯"] # 0x1d712 - - "𝜓": [tc: "GL⠽"] # 0x1d713 - - "𝜔": [tc: "GL⠺"] # 0x1d714 - - - "𝜕": [tc: "VL⠙"] # 0x1d715 - - - "𝜖": [tc: "GL⠑"] # 0x1d716 - - - "𝜗": [tc: "VL⠹"] # 0x1d717 - - - "𝜘": [tc: "GL⠅"] # 0x1d718 - - "𝜙": [tc: "GL⠋"] # 0x1d719 - - "𝜚": [tc: "GL⠗"] # 0x1d71a - - "𝜛": [tc: "GL⠏"] # 0x1d71b - - # --- Nemeth Default Alphabet monospace chars. --- - - - "𝙰-𝚉": # 0x1d670 - 0x1d689 - - tc: "" - - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 - - tc: "" - - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff - - tc: "" - - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" - - - # --- Nemeth Default Alphabet negative-circled chars. --- - - "⓿": [t: "⠫⠸⠉⠸⠫⠼⠴⠻"] # 0x24ff - - "❶": [t: "⠫⠸⠉⠸⠫⠼⠂⠻"] # 0x2776 - - "❷": [t: "⠫⠸⠉⠸⠫⠼⠆⠻"] # 0x2777 - - "❸": [t: "⠫⠸⠉⠸⠫⠼⠒⠻"] # 0x2778 - - "❹": [t: "⠫⠸⠉⠸⠫⠼⠲⠻"] # 0x2779 - - "❺": [t: "⠫⠸⠉⠸⠫⠼⠢⠻"] # 0x277a - - "❻": [t: "⠫⠸⠉⠸⠫⠼⠖⠻"] # 0x277b - - "❼": [t: "⠫⠸⠉⠸⠫⠼⠶⠻"] # 0x277c - - "❽": [t: "⠫⠸⠉⠸⠫⠼⠦⠻"] # 0x277d - - "❾": [t: "⠫⠸⠉⠸⠫⠼⠔⠻"] # 0x277e - - "❿": [t: "⠫⠸⠉⠸⠫⠼⠂⠴⠻"] # 0x277f - - "⓫": [t: "⠫⠸⠉⠸⠫⠼⠂⠂⠻"] # 0x24eb - - "⓬": [t: "⠫⠸⠉⠸⠫⠼⠂⠆⠻"] # 0x24ec - - "⓭": [t: "⠫⠸⠉⠸⠫⠼⠂⠒⠻"] # 0x24ed - - "⓮": [t: "⠫⠸⠉⠸⠫⠼⠂⠲⠻"] # 0x24ee - - "⓯": [t: "⠫⠸⠉⠸⠫⠼⠂⠢⠻"] # 0x24ef - - "⓰": [t: "⠫⠸⠉⠸⠫⠼⠂⠖⠻"] # 0x24f0 - - "⓱": [t: "⠫⠸⠉⠸⠫⠼⠂⠶⠻"] # 0x24f1 - - "⓲": [t: "⠫⠸⠉⠸⠫⠼⠂⠦⠻"] # 0x24f2 - - "⓳": [t: "⠫⠸⠉⠸⠫⠼⠂⠔⠻"] # 0x24f3 - - "⓴": [t: "⠫⠸⠉⠸⠫⠼⠆⠴⠻"] # 0x24f4 - - - "🅐": [t: "⠫⠸⠉⠸⠫⠠⠁⠻"] # 0x1f150 - - "🅑": [t: "⠫⠸⠉⠸⠫⠠⠃⠻"] # 0x1f151 - - "🅒": [t: "⠫⠸⠉⠸⠫⠠⠉⠻"] # 0x1f152 - - "🅓": [t: "⠫⠸⠉⠸⠫⠠⠙⠻"] # 0x1f153 - - "🅔": [t: "⠫⠸⠉⠸⠫⠠⠑⠻"] # 0x1f154 - - "🅕": [t: "⠫⠸⠉⠸⠫⠠⠋⠻"] # 0x1f155 - - "🅖": [t: "⠫⠸⠉⠸⠫⠠⠛⠻"] # 0x1f156 - - "🅗": [t: "⠫⠸⠉⠸⠫⠠⠓⠻"] # 0x1f157 - - "🅘": [t: "⠫⠸⠉⠸⠫⠠⠊⠻"] # 0x1f158 - - "🅙": [t: "⠫⠸⠉⠸⠫⠠⠚⠻"] # 0x1f159 - - "🅚": [t: "⠫⠸⠉⠸⠫⠠⠅⠻"] # 0x1f15a - - "🅛": [t: "⠫⠸⠉⠸⠫⠠⠇⠻"] # 0x1f15b - - "🅜": [t: "⠫⠸⠉⠸⠫⠠⠍⠻"] # 0x1f15c - - "🅝": [t: "⠫⠸⠉⠸⠫⠠⠝⠻"] # 0x1f15d - - "🅞": [t: "⠫⠸⠉⠸⠫⠠⠕⠻"] # 0x1f15e - - "🅟": [t: "⠫⠸⠉⠸⠫⠠⠏⠻"] # 0x1f15f - - "🅠": [t: "⠫⠸⠉⠸⠫⠠⠟⠻"] # 0x1f160 - - "🅡": [t: "⠫⠸⠉⠸⠫⠠⠗⠻"] # 0x1f161 - - "🅢": [t: "⠫⠸⠉⠸⠫⠠⠎⠻"] # 0x1f162 - - "🅣": [t: "⠫⠸⠉⠸⠫⠠⠞⠻"] # 0x1f163 - - "🅤": [t: "⠫⠸⠉⠸⠫⠠⠥⠻"] # 0x1f164 - - "🅥": [t: "⠫⠸⠉⠸⠫⠠⠧⠻"] # 0x1f165 - - "🅦": [t: "⠫⠸⠉⠸⠫⠠⠺⠻"] # 0x1f166 - - "🅧": [t: "⠫⠸⠉⠸⠫⠠⠭⠻"] # 0x1f167 - - "🅨": [t: "⠫⠸⠉⠸⠫⠠⠽⠻"] # 0x1f168 - - "🅩": [t: "⠫⠸⠉⠸⠫⠠⠵⠻"] # 0x1f169 - - # --- Nemeth Default Alphabet negative-circled-sans-serif chars. --- - - "🄌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10c - - "➊": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠻"] # 0x278a - - "➋": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠆⠻"] # 0x278b - - "➌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠒⠻"] # 0x278c - - "➍": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠲⠻"] # 0x278d - - "➎": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠢⠻"] # 0x278e - - "➏": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠖⠻"] # 0x278f - - "➐": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠶⠻"] # 0x2790 - - "➑": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠦⠻"] # 0x2791 - - "➒": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠔⠻"] # 0x2792 - - "➓": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2793 - - # --- Nemeth Default Alphabet negative-squared chars. --- - - "🅰-🆉": # 0x1f170 - 0x1f189 - - tc: "⠫⠸⠲⠸⠫⠠" - - spell: "translate('.', '🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠻" - - # --- Nemeth Default Alphabet parenthesized chars. --- - - - "⑴-⑼": # 0x2474 - 0x247c - - tc: "⠷" - - spell: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" - - tc: "⠾" - - - "⑽": [t: "⠷⠂⠴⠾"] # 0x247d - - "⑾": [t: "⠷⠂⠂⠾"] # 0x247e - - "⑿": [t: "⠷⠂⠆⠾"] # 0x247f - - "⒀": [t: "⠷⠂⠒⠾"] # 0x2480 - - "⒁": [t: "⠷⠂⠲⠾"] # 0x2481 - - "⒂": [t: "⠷⠂⠢⠾"] # 0x2482 - - "⒃": [t: "⠷⠂⠖⠾"] # 0x2483 - - "⒄": [t: "⠷⠂⠶⠾"] # 0x2484 - - "⒅": [t: "⠷⠂⠦⠾"] # 0x2485 - - "⒆": [t: "⠷⠂⠔⠾"] # 0x2486 - - "⒇": [t: "⠷⠆⠴⠾"] # 0x2487 - - - "🄐-🄩": # 0x1f110 - 0x1f129 - - tc: "⠷⠠" - - spell: "translate('.', '🄐🄑🄒🄓🄔🄕🄖🄗🄘🄙🄚🄛🄜🄝🄞🄟🄠🄡🄢🄣🄤🄥🄦🄧🄨🄩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠾" - - - "⒜-⒵": # 0x249c - 0x24b5 - - tc: "⠷" - - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" - - tc: "⠾" - - # --- Nemeth Default Alphabet period chars. --- - - "🄀": [t: "⠴⠸⠲"] # 0x1f100 - - "⒈": [t: "⠂⠸⠲"] # 0x2488 - - "⒉": [t: "⠆⠸⠲"] # 0x2489 - - "⒊": [t: "⠒⠸⠲"] # 0x248a - - "⒋": [t: "⠲⠸⠲"] # 0x248b - - "⒌": [t: "⠢⠸⠲"] # 0x248c - - "⒍": [t: "⠖⠸⠲"] # 0x248d - - "⒎": [t: "⠶⠸⠲"] # 0x248e - - "⒏": [t: "⠦⠸⠲"] # 0x248f - - "⒐": [t: "⠔⠸⠲"] # 0x2490 - - - "⒑": [t: "⠂⠴⠸⠲"] # 0x2491 - - "⒒": [t: "⠂⠂⠸⠲"] # 0x2492 - - "⒓": [t: "⠂⠆⠸⠲"] # 0x2493 - - "⒔": [t: "⠂⠒⠸⠲"] # 0x2494 - - "⒕": [t: "⠂⠲⠸⠲"] # 0x2495 - - "⒖": [t: "⠂⠢⠸⠲"] # 0x2496 - - "⒗": [t: "⠂⠖⠸⠲"] # 0x2497 - - "⒘": [t: "⠂⠶⠸⠲"] # 0x2498 - - "⒙": [t: "⠂⠦⠸⠲"] # 0x2499 - - "⒚": [t: "⠂⠔⠸⠲"] # 0x249a - - "⒛": [t: "⠆⠴⠸⠲"] # 0x249b - - # --- Nemeth Default Alphabet sans-serif chars. --- - - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 - - tc: "SE" - - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 - - tc: "SE" - - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb - - tc: "S" - - spell: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" - - # --- Nemeth Default Alphabet sans-serif-bold chars. --- - - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed - - tc: "SBE" - - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝗮-𝘇": # 0x1d5ee - 0x1d607 - - tc: "SBE" - - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝝖-𝝮": # 0x1d756 - 0x1d76e - - tc: "SBG" - - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝝰-𝞀": # 0x1d770 - 0x1d780 - - tc: "SBG" - - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀', 'αβγδεζηθικλμνξοπρ')" - - - "𝝯": [tc: "SBGL⠫"] # 0x1d76f - - - "𝞁": [tc: "SBVL⠎"] # 0x1d781 - - - "𝞂-𝞄": # 0x1d782 - 0x1d784 - - tc: "SBG" - - spell: "translate('.', '𝞂𝞃𝞄', 'στυ')" - - - "𝞅": [tc: "SBVL⠋"] # 0x1d785 - - - "𝞆-𝞈": # 0x1d786 - 0x1d788 - - tc: "SBG" - - spell: "translate('.', '𝞆𝞇𝞈', 'χψω')" - - - "𝞉": [tc: "⠠⠨⠸⠈⠙"] # 0x1d789 - - "𝞊": [tc: "SBGL⠑"] # 0x1d78a - - "𝞋": [tc: "SBVL⠹"] # 0x1d78b - - - "𝞌-𝞏": # 0x1d78c - 0x1d78f - - tc: "SBG" - - spell: "translate('.', '𝞌𝞍𝞎𝞏', 'κφρπ')" - - - "𝟬-𝟵": # 0x1d7ec - 0x1d7f5 - - tc: "SB" - - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" - - # --- Nemeth Default Alphabet sans-serif-bold-italic chars. --- - - - "𝘼-𝙕": # 0x1d63c - 0x1d655 - - tc: "SBE" - - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝙖-𝙯": # 0x1d656 - 0x1d66f - - tc: "SBE" - - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 - - tc: "SBG" - - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝞩": [t: "SBGL⠫"] # 0x1d7a9 - - - "𝞪-𝞺": # 0x1d7aa - 0x1d7ba - - tc: "SBG" - - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺', 'αβγδεζηθικλμνξοπρ')" - - - "𝞻": [t: "SBVL⠎"] # 0x1d7bb - - "𝞼": [t: "SBGL⠎"] # 0x1d7bc - - "𝞽": [t: "SBGL⠞"] # 0x1d7bd - - "𝞾": [t: "SBGL⠥"] # 0x1d7be - - "𝞿": [t: "SBVL⠋"] # 0x1d7bf - - - "𝟀": [t: "SBGL⠯"] # 0x1d7c0 - - "𝟁": [t: "SBGL⠽"] # 0x1d7c1 - - "𝟂": [t: "SBGL⠺"] # 0x1d7c2 - - "𝟃": [t: "SBVL⠙"] # 0x1d7c3 - - - "𝟄": [t: "SBGL⠑"] # 0x1d7c4 - - "𝟅": [t: "SBVL⠹"] # 0x1d7c5 - - - "𝟆": [t: "SBGL⠅"] # 0x1d7c6 - - "𝟇": [t: "SBGL⠋"] # 0x1d7c7 - - "𝟈": [t: "SBGL⠗"] # 0x1d7c8 - - "𝟉": [t: "SBGL⠏"] # 0x1d7c9 - - # --- Nemeth Default Alphabet sans-serif-italic chars. --- - - "𝘈-𝘡": # 0x1d608 - 0x1d621 - - tc: "SE" - - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝘢-𝘻": # 0x1d622 - 0x1d63b - - tc: "SE" - - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet script chars. --- - - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 - - tc: "TE" - - spell: "translate('.', '𝒜𝒞𝒟𝒢𝒥𝒦𝒩𝒪𝒫𝒬𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ACDGJKNOPQSTUVWXYZ')" - - - "ℬ": [t: "TECL⠃"] # 0x212c - - "ℰ": [t: "TECL⠑"] # 0x2130 - - "ℱ": [t: "TECL⠋"] # 0x2131 - - "ℋ": [t: "TECL⠓"] # 0x210b - - "ℐ": [t: "TECL⠊"] # 0x2110 - - "ℒ": [t: "TECL⠇"] # 0x2112 - - "ℳ": [t: "TECL⠍"] # 0x2133 - - "ℛ": [t: "TECL⠗"] # 0x211b - - - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf - - tc: "TE" - - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒻𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdfhijklmnpqrstuvwxyz')" - - - "ℯ": [t: "TEL⠑"] # 0x212f - - "ℊ": [t: "TEL⠛"] # 0x210a - - "ℴ": [t: "TEL⠕"] # 0x2134 - - # --- Nemeth Default Alphabet squared chars. --- - - - "🄰": [t: "⠫⠲⠸⠫⠠⠁⠻"] # 0x1f130 - - "🄱": [t: "⠫⠲⠸⠫⠠⠃⠻"] # 0x1f131 - - "🄲": [t: "⠫⠲⠸⠫⠠⠉⠻"] # 0x1f132 - - "🄳": [t: "⠫⠲⠸⠫⠠⠙⠻"] # 0x1f133 - - "🄴": [t: "⠫⠲⠸⠫⠠⠑⠻"] # 0x1f134 - - "🄵": [t: "⠫⠲⠸⠫⠠⠋⠻"] # 0x1f135 - - "🄶": [t: "⠫⠲⠸⠫⠠⠛⠻"] # 0x1f136 - - "🄷": [t: "⠫⠲⠸⠫⠠⠓⠻"] # 0x1f137 - - "🄸": [t: "⠫⠲⠸⠫⠠⠊⠻"] # 0x1f138 - - "🄹": [t: "⠫⠲⠸⠫⠠⠚⠻"] # 0x1f139 - - "🄺": [t: "⠫⠲⠸⠫⠠⠅⠻"] # 0x1f13a - - "🄻": [t: "⠫⠲⠸⠫⠠⠇⠻"] # 0x1f13b - - "🄼": [t: "⠫⠲⠸⠫⠠⠍⠻"] # 0x1f13c - - "🄽": [t: "⠫⠲⠸⠫⠠⠝⠻"] # 0x1f13d - - "🄾": [t: "⠫⠲⠸⠫⠠⠕⠻"] # 0x1f13e - - "🄿": [t: "⠫⠲⠸⠫⠠⠏⠻"] # 0x1f13f - - "🅀": [t: "⠫⠲⠸⠫⠠⠟⠻"] # 0x1f140 - - "🅁": [t: "⠫⠲⠸⠫⠠⠗⠻"] # 0x1f141 - - "🅂": [t: "⠫⠲⠸⠫⠠⠎⠻"] # 0x1f142 - - "🅃": [t: "⠫⠲⠸⠫⠠⠞⠻"] # 0x1f143 - - "🅄": [t: "⠫⠲⠸⠫⠠⠥⠻"] # 0x1f144 - - "🅅": [t: "⠫⠲⠸⠫⠠⠧⠻"] # 0x1f145 - - "🅆": [t: "⠫⠲⠸⠫⠠⠺⠻"] # 0x1f146 - - "🅇": [t: "⠫⠲⠸⠫⠠⠭⠻"] # 0x1f147 - - "🅈": [t: "⠫⠲⠸⠫⠠⠽⠻"] # 0x1f148 - - "🅉": [t: "⠫⠲⠸⠫⠠⠵⠻"] # 0x1f149 - - # --- Nemeth Default Alphabet sub chars. --- - - - "₀": [t: "⠰⠴"] # 0x2080 - - "₁": [t: "⠰⠂"] # 0x2081 - - "₂": [t: "⠰⠆"] # 0x2082 - - "₃": [t: "⠰⠒"] # 0x2083 - - "₄": [t: "⠰⠲"] # 0x2084 - - "₅": [t: "⠰⠢"] # 0x2085 - - "₆": [t: "⠰⠖"] # 0x2086 - - "₇": [t: "⠰⠶"] # 0x2087 - - "₈": [t: "⠰⠦"] # 0x2088 - - "₉": [t: "⠰⠔"] # 0x2089 - - # --- Nemeth Default Alphabet super chars. --- - - - "⁰": [t: "⠘⠴"] # 0x2070 - - "¹": [t: "⠘⠂"] # 0xb9 - - "²": [t: "⠘⠆"] # 0xb2 - - "³": [t: "⠘⠒"] # 0xb3 - - "⁴": [t: "⠘⠲"] # 0x2074 - - "⁵": [t: "⠘⠢"] # 0x2075 - - "⁶": [t: "⠘⠖"] # 0x2076 - - "⁷": [t: "⠘⠶"] # 0x2077 - - "⁸": [t: "⠘⠦"] # 0x2078 - - "⁹": [t: "⠘⠔"] # 0x2079 - - # --- Nemeth Default Characters latin-lower-phonetic chars. --- - - - "ø": [t: "⠈⠕"] # 0xf8 - - # --- Nemeth Default Characters math_geometry chars. --- - - - "─": [t: "⠱"] # 0x2500 - - "│": [t: "⠳"] # 0x2502 - - "▰": [t: "⠫⠸⠛"] # 0x25b0 - - "▱": [t: "⠫⠛"] # 0x25b1 - - "▽": [t: "⠨⠫"] # 0x25bd - - "◆": [t: "⠫⠸⠙"] # 0x25c6 - - "◇": [t: "⠫⠙"] # 0x25c7 - - "◈": [t: "⠫⠸⠙"] # 0x25c8 - - "◉": [t: "⠫⠸⠉"] # 0x25c9 - - "◊": [t: "⠫⠙"] # 0x25ca - - "⬟": [t: "⠫⠸⠢"] # 0x2b1f - - "⬠": [t: "⠫⠢"] # 0x2b20 - - "⬡": [t: "⠫⠖"] # 0x2b21 - - "⬢": [t: "⠫⠸⠖"] # 0x2b22 - - "⬣": [t: "⠫⠸⠖"] # 0x2b23 - - "⬤": [t: "⠫⠸⠉"] # 0x2b24 - - "⬥": [t: "⠫⠸⠙"] # 0x2b25 - - # --- Nemeth Default Characters math_symbols chars. --- - - - "\"": [t: "⠄⠄"] # 0x22 - - "¡": [t: "⠖"] # 0xa1 - - "¢": [t: "⠈⠉"] # 0xa2 - - "£": [t: "⠈⠇"] # 0xa3 - - "¤": [t: "⠈⠫"] # 0xa4 - - "¥": [t: "⠈⠽"] # 0xa5 - - "§": [t: "⠈⠠⠎"] # 0xa7 - - "©": [t: "⠘⠉"] # 0xa9 (Copyright) - - "ª": [t: "⠸⠰⠁"] # 0xaa - - "«": [t: "⠐⠅⠈⠐⠅⠻"] # 0xab - - "®": [t: "⠘⠗"] # 0xae - - "´": [t: "⠈"] # 0xb4 - - "µ": [t: "⠨⠍"] # 0xb5 - - "¶": [t: "⠈⠠⠏"] # 0xb6 - - "¿": [t: "⠦"] # 0xbf - - "ʹ": [t: "⠄"] # 0x2b9 - - "ʺ": [t: "⠄⠄"] # 0x2ba - - "˙": [t: "⠡"] # 0x2d9 - - "˜": [t: "⠈⠱"] # 0x2dc - - "‐": [t: "⠤"] # 0x2010 - - "‑": [t: "⠤"] # 0x2011 - - "‒": [t: "⠤⠤"] # 0x2012 - - "–": [t: "⠤⠤"] # 0x2013 - - "‖": [t: "⠳⠳"] # 0x2016 - - "†": [t: "⠸⠻"] # 0x2020 - - "‡": [t: "⠸⠸⠻"] # 0x2021 - - "•": [t: "⠡"] # 0x2022 - - "․": [t: "⠄"] # 0x2024 - - "‥": [t: "⠄⠄"] # 0x2025 - - "′": [t: "⠄"] # 0x2032 - - "″": [t: "⠄⠄"] # 0x2033 - - "‴": [t: "⠄⠄."] # 0x2034 (Triple prime) - - "⁗": [t: "⠄⠄.."] # 0x2057 (Quadruple prime) - - "‼": [t: "⠖⠖"] # 0x203c - - "‾": [t: "⠱"] # 0x203e - - "⁇": [t: "⠹⠹"] # 0x2047 - - "⁈": [t: "⠹⠖"] # 0x2048 - - "⁉": [t: "⠖⠹"] # 0x2049 - - "∀": [t: "⠈⠯"] # 0x2200 - - "∃": [t: "⠈⠿"] # 0x2203 - - "∄": [t: "⠌⠈⠿"] # 0x2204 - - "∅": [t: "⠸⠴"] # 0x2205 - - "∈": [t: "⠈⠑"] # 0x2208 - - "∉": [t: "⠌⠈⠑"] # 0x2209 - - "∊": [t: "⠈⠑"] # 0x220a - - "∋": [t: "⠈⠢"] # 0x220b - - "∌": [t: "⠌⠈⠢"] # 0x220c - - "∍": [t: "⠈⠢"] # 0x220d - - "∎": [t: "⠸⠳"] # 0x220e - - "−": [t: "⠤"] # 0x2212 - - "∓": [t: "⠤⠬"] # 0x2213 - - "∕": [t: "⠸⠌"] # 0x2215 - - "∖": [t: "⠸⠡"] # 0x2216 - - "∗": [t: "⠈⠼"] # 0x2217 - - "∘": [t: "⠨⠡"] # 0x2218 - - "∝": [t: "⠸⠿"] # 0x221d - - "∞": [t: "⠠⠿"] # 0x221e - - "∟": [t: "⠫⠪⠨⠗⠻"] # 0x221f - - "∠": [t: "⠫⠪"] # 0x2220 - - "∢": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2222 - - "∣": [t: "⠳"] # 0x2223 - - "∤": [t: "⠌⠳"] # 0x2224 - - "∥": [t: "⠫⠇"] # 0x2225 - - "∦": [t: "⠌⠫⠇"] # 0x2226 - - "∧": [t: "⠈⠩"] # 0x2227 - - "∨": [t: "⠈⠬"] # 0x2228 - - "∩": [t: "⠨⠩"] # 0x2229 - - "∪": [t: "⠨⠬"] # 0x222a - - "∫": [t: "⠮"] # 0x222b - - "∬": [t: "⠮⠮"] # 0x222c - - "∭": [t: "⠮⠮⠮"] # 0x222d - - "∮": [t: "⠮⠈⠫⠉⠻"] # 0x222e - - "∲": [t: "⠮⠈⠫⠪⠢⠔⠻"] # 0x2232 - - "∳": [t: "⠮⠈⠫⠢⠔⠕⠻"] # 0x2233 - - "∴": [t: "⠠⠡"] # 0x2234 - - "∵": [t: "⠈⠌"] # 0x2235 - - "∶": [t: "⠐⠂"] # 0x2236 - - "∷": [t: "⠰⠆"] # 0x2237 - - "∸": [t: "⠨⠤"] # 0x2238 - - "∼": [t: "⠈⠱"] # 0x223c - - "≁": [t: "⠌⠈⠱"] # 0x2241 - - "≂": [t: "⠱⠈⠱"] # 0x2242 - - "≃": [t: "⠈⠱⠱"] # 0x2243 - - "≄": [t: "⠌⠈⠱⠱"] # 0x2244 - - "≅": [t: "⠈⠱⠨⠅"] # 0x2245 - - "≆": [t: "⠈⠱⠌⠨⠅"] # 0x2246 - - "≇": [t: "⠌⠈⠱⠨⠅"] # 0x2247 - - "≈": [t: "⠈⠱⠈⠱"] # 0x2248 - - "≉": [t: "⠌⠈⠱⠈⠱"] # 0x2249 - - "≊": [t: "⠈⠱⠈⠱⠱"] # 0x224a - - "≍": [t: "⠈⠣⠠⠣"] # 0x224d - - "≎": [t: "⠈⠣⠠⠣"] # 0x224e - - "≐": [t: "⠐⠨⠅⠣⠡⠻"] # 0x2250 - - "≑": [t: "⠐⠨⠅⠩⠡⠣⠡⠻"] # 0x2251 - - "≗": [t: "⠐⠨⠅⠣⠨⠡⠻"] # 0x2257 - - "≘": [t: "⠐⠨⠅⠣⠫⠁⠻"] # 0x2258 - - "≙": [t: "⠐⠨⠅⠣⠸⠣⠻"] # 0x2259 - - "≚": [t: "⠐⠨⠅⠣⠸⠩⠻"] # 0x225a - - "≛": [t: "⠐⠨⠅⠣⠫⠸⠎⠻"] # 0x225b - - "≜": [t: "⠐⠨⠅⠣⠫⠞⠻"] # 0x225c - - "≝": [t: "⠐⠨⠅⠣⠙⠑⠋⠻"] # 0x225d - - "≞": [t: "⠐⠨⠅⠣⠍⠻"] # 0x225e - - "≟": [t: "⠐⠨⠅⠣⠸⠦⠻"] # 0x225f - - "≠": [t: "⠌⠨⠅"] # 0x2260 - - "≢": [t: "⠌⠸⠇"] # 0x2262 - - "≤": [t: "⠐⠅⠱"] # 0x2264 - - "≥": [t: "⠨⠂⠱"] # 0x2265 - - "≦": [t: "⠐⠅⠨⠅"] # 0x2266 - - "≧": [t: "⠨⠂⠨⠅"] # 0x2267 - - "≨": [t: "⠐⠅⠌⠨⠅"] # 0x2268 - - "≩": [t: "⠨⠂⠌⠨⠅"] # 0x2269 - - "≪": [t: "⠐⠅⠈⠐⠅⠻"] # 0x226a - - "≫": [t: "⠨⠂⠈⠨⠂⠻"] # 0x226b - - "≭": [t: "⠌⠈⠣⠠⠣"] # 0x226d - - "≮": [t: "⠌⠐⠅"] # 0x226e - - "≯": [t: "⠌⠨⠂"] # 0x226f - - "≰": [t: "⠌⠐⠅⠱"] # 0x2270 - - "≱": [t: "⠌⠨⠂⠱"] # 0x2271 - - "≲": [t: "⠐⠅⠈⠱"] # 0x2272 - - "≳": [t: "⠨⠂⠈⠱"] # 0x2273 - - "≴": [t: "⠌⠐⠅⠈⠱"] # 0x2274 - - "≵": [t: "⠌⠨⠂⠈⠱"] # 0x2275 - - "≶": [t: "⠐⠅⠨⠂"] # 0x2276 - - "≷": [t: "⠨⠂⠐⠅"] # 0x2277 - - "≸": [t: "⠌⠐⠅⠨⠂"] # 0x2278 - - "≹": [t: "⠌⠨⠂⠐⠅"] # 0x2279 - - "≺": [t: "⠨⠐⠅"] # 0x227a - - "≻": [t: "⠨⠨⠂"] # 0x227b - - "≼": [t: "⠨⠐⠅⠱"] # 0x227c - - "≽": [t: "⠨⠨⠐⠱"] # 0x227d - - "≾": [t: "⠨⠐⠅⠈⠱"] # 0x227e - - "≿": [t: "⠨⠨⠂⠈⠱"] # 0x227f - - "⊀": [t: "⠌⠨⠐⠅"] # 0x2280 - - "⊁": [t: "⠌⠨⠐⠅"] # 0x2281 - - "⊂": [t: "⠸⠐⠅"] # 0x2282 - - "⊃": [t: "⠸⠨⠂"] # 0x2283 - - "⊄": [t: "⠌⠸⠐⠅"] # 0x2284 - - "⊅": [t: "⠌⠸⠨⠂"] # 0x2285 - - "⊆": [t: "⠸⠐⠅⠱"] # 0x2286 - - "⊇": [t: "⠸⠨⠂⠱"] # 0x2287 - - "⊈": [t: "⠌⠸⠐⠅⠱"] # 0x2288 - - "⊉": [t: "⠌⠸⠨⠂⠱"] # 0x2289 - - "⊊": [t: "⠸⠐⠅⠌⠨⠅"] # 0x228a - - "⊋": [t: "⠸⠨⠂⠌⠨⠅"] # 0x228b - - "⊥": [t: "⠫⠏"] # 0x22a5 - - "⋀": [t: "⠈⠩"] # 0x22c0 - - "⋁": [t: "⠈⠬"] # 0x22c1 - - "⋂": [t: "⠨⠩"] # 0x22c2 - - "⋃": [t: "⠨⠬"] # 0x22c3 - - "⋄": [t: "⠫⠙"] # 0x22c4 - - "⋜": [t: "⠱⠐⠅"] # 0x22dc - - "⋝": [t: "⠱⠨⠂"] # 0x22dd - - "⟂": [t: "⠫⠏"] # 0x27c2 - - "⦀": [t: "⠳⠳⠳"] # 0x2980 - - "⦶": [t: "⠫⠉⠸⠫⠳⠻"] # 0x29b6 - - "⦷": [t: "⠫⠉⠸⠫⠫⠇⠻"] # 0x29b7 - - "⦸": [t: "⠫⠉⠸⠫⠸⠡⠻"] # 0x29b8 - - "⦹": [t: "⠫⠉⠸⠫⠫⠏⠻"] # 0x29b9 - - "⦿": [t: "⠫⠉⠸⠫⠔⠔⠻"] # 0x29bf - - "⧀": [t: "⠫⠉⠸⠫⠐⠅⠻"] # 0x29c0 - - "⧁": [t: "⠫⠉⠸⠫⠨⠂⠻"] # 0x29c1 - - "⨀": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2a00 - - "⨁": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2a01 - - "⨂": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2a02 - - "⪯": [t: "⠨⠐⠅⠱"] # 0x2aaf - - "⪰": [t: "⠨⠨⠂⠱"] # 0x2ab0 - - "⪱": [t: "⠨⠐⠅⠌⠱"] # 0x2ab1 - - "⪲": [t: "⠨⠨⠂⠌⠱"] # 0x2ab2 - - "⪳": [t: "⠨⠐⠅⠨⠅"] # 0x2ab3 - - "⪴": [t: "⠨⠨⠂⠨⠅"] # 0x2ab4 - - "⪵": [t: "⠨⠐⠅⠌⠨⠅"] # 0x2ab5 - - "⪶": [t: "⠨⠨⠂⠌⠨⠅"] # 0x2ab6 - - "⪷": [t: "⠨⠐⠅⠈⠱⠈⠱"] # 0x2ab7 - - "⪸": [t: "⠨⠨⠂⠈⠱⠈⠱"] # 0x2ab8 - - "⪹": [t: "⠨⠐⠅⠌⠈⠱⠈⠱"] # 0x2ab9 - - "⪺": [t: "⠨⠨⠂⠌⠈⠱⠈⠱"] # 0x2aba - - "⪻": [t: "⠨⠐⠅⠈⠨⠐⠅⠻"] # 0x2abb - - "⪼": [t: "⠨⠨⠂⠈⠨⠨⠂⠻"] # 0x2abc - - "﹩": [t: "⠈⠎"] # 0xfe69 - - "$": [t: "⠈⠎"] # 0xff04 - - "_": [t: "⠱"] # 0xff3f - - "~": [t: "⠈⠱"] # 0xff5e - - "¢": [t: "⠈⠉"] # 0xffe0 - - "£": [t: "⠈⠇"] # 0xffe1 - - " ̄": [t: "⠱"] # 0xffe3 - - "¥": [t: "⠈⠽"] # 0xffe5 - - # --- Nemeth Default Characters rest chars. --- - - - "ℵ": [t: "⠠⠠⠁"] # 0x2135 - - "ℶ": [t: "⠠⠠⠃"] # 0x2136 - - "ℷ": [t: "⠠⠠⠉"] # 0x2137 - - "ℸ": [t: "⠠⠠⠙"] # 0x2138 - - "à": [t: "⠈⠁"] # 0xe0 - - "á": [t: "⠈⠁"] # 0xe1 - - "â": [t: "⠈⠁"] # 0xe2 - - "ã": [t: "⠈⠁"] # 0xe3 - - "ä": [t: "⠈⠁"] # 0xe4 - - "å": [t: "⠈⠁"] # 0xe5 - - "ç": [t: "⠈⠉"] # 0xe7 - - "è": [t: "⠈⠑"] # 0xe8 - - "é": [t: "⠈⠑"] # 0xe9 - - "ê": [t: "⠈⠑"] # 0xea - - "ë": [t: "⠈⠑"] # 0xeb - - "ì": [t: "⠈⠊"] # 0xec - - "í": [t: "⠈⠊"] # 0xed - - "î": [t: "⠈⠊"] # 0xee - - "ï": [t: "⠈⠊"] # 0xef - - "ñ": [t: "⠈⠝"] # 0xf1 - - "ò": [t: "⠈⠕"] # 0xf2 - - "ó": [t: "⠈⠕"] # 0xf3 - - "ô": [t: "⠈⠕"] # 0xf4 - - "õ": [t: "⠈⠕"] # 0xf5 - - "ö": [t: "⠈⠕"] # 0xf6 - - "ù": [t: "⠈⠥"] # 0xf9 - - "ú": [t: "⠈⠥"] # 0xfa - - "û": [t: "⠈⠥"] # 0xfb - - "ü": [t: "⠈⠥"] # 0xfc - - "ý": [t: "⠈⠽"] # 0xfd - - "ÿ": [t: "⠈⠽"] # 0xff - - - "À": [t: "⠠⠈⠁"] # 0xc0 - - "Á": [t: "⠠⠈⠁"] # 0xc1 - - "Â": [t: "⠠⠈⠁"] # 0xc2 - - "Ã": [t: "⠠⠈⠁"] # 0xc3 - - "Ä": [t: "⠠⠈⠁"] # 0xc4 - - "Å": [t: "⠠⠈⠁"] # 0xc5 - - "Ç": [t: "⠠⠈⠉"] # 0xc7 - - "È": [t: "⠠⠈⠑"] # 0xc8 - - "É": [t: "⠠⠈⠑"] # 0xc9 - - "Ê": [t: "⠠⠈⠑"] # 0xca - - "Ë": [t: "⠠⠈⠑"] # 0xcb - - "Ì": [t: "⠠⠈⠊"] # 0xcc - - "Í": [t: "⠠⠈⠊"] # 0xcd - - "Î": [t: "⠠⠈⠊"] # 0xce - - "Ï": [t: "⠠⠈⠊"] # 0xcf - - "Ñ": [t: "⠠⠈⠝"] # 0xd1 - - "Ò": [t: "⠠⠈⠕"] # 0xd2 - - "Ó": [t: "⠠⠈⠕"] # 0xd3 - - "Ô": [t: "⠠⠈⠕"] # 0xd4 - - "Ö": [t: "⠠⠈⠕"] # 0xd6 - - "Ù": [t: "⠠⠈⠥"] # 0xd9 - - "Ú": [t: "⠠⠈⠥"] # 0xda - - "Û": [t: "⠠⠈⠥"] # 0xdb - - "Ü": [t: "⠠⠈⠥"] # 0xdc - - "Ý": [t: "⠠⠈⠽"] # 0xdd - - "←": [t: "⠫⠪⠒⠒"] # 0x2190 - - - "↑": # 0x2191 - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠣"] - else: [t: "⠫⠣⠒⠒⠕"] - - - "↓": # 0x2193 - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠩"] - else: [t: "⠫⠩⠒⠒⠕"] - - - "↔": [t: "⠫⠪⠒⠒⠕"] # 0x2194 - - "↕": [t: "⠫⠣⠩⠪⠒⠒⠕"] # 0x2195 - - "↖": [t: "⠫⠘⠪⠒⠒"] # 0x2196 - - "↗": [t: "⠫⠘⠒⠒⠕"] # 0x2197 - - "↘": [t: "⠫⠰⠒⠒⠕"] # 0x2198 - - "↙": [t: "⠫⠰⠪⠒⠒"] # 0x2199 - - "↚": [t: "⠳⠈⠫⠪⠒⠒⠻"] # 0x219a - - "↛": [t: "⠳⠈⠫⠒⠒⠕⠻"] # 0x219b - - "↜": [t: "⠫⠪⠔⠒⠢"] # 0x219c - - "↝": [t: "⠫⠔⠒⠢⠕"] # 0x219d - - "↞": [t: "⠫⠪⠪⠒⠒"] # 0x219e - - "↟": [t: "⠫⠣⠒⠒⠕⠕"] # 0x219f - - "↠": [t: "⠫⠒⠒⠕⠕"] # 0x21a0 - - "↡": [t: "⠫⠩⠒⠒⠕⠕"] # 0x21a1 - - "↢": [t: "⠫⠪⠒⠒⠠⠽"] # 0x21a2 - - "↣": [t: "⠫⠠⠯⠒⠒⠕"] # 0x21a3 - - "↤": [t: "⠫⠪⠒⠒⠳"] # 0x21a4 - - "↥": [t: "⠫⠣⠳⠒⠒⠕"] # 0x21a5 - - "↦": [t: "⠫⠳⠒⠒⠕"] # 0x21a6 - - "↧": [t: "⠫⠩⠳⠒⠒⠕"] # 0x21a7 - - "↨": [t: "⠫⠪⠒⠳⠒⠕"] # 0x21a8 - - "↩": [t: "⠫⠪⠒⠒⠠⠕"] # 0x21a9 - - "↪": [t: "⠫⠠⠪⠒⠒⠕"] # 0x21aa - - "↫": [t: "⠫⠪⠒⠒⠨⠡"] # 0x21ab - - "↬": [t: "⠫⠨⠡⠒⠒⠕"] # 0x21ac - - "↭": [t: "⠫⠪⠔⠒⠢⠕"] # 0x21ad - - "↮": [t: "⠳⠈⠫⠪⠒⠒⠕"] # 0x21ae - - "↯": [t: "⠫⠩⠔⠢⠔"] # 0x21af - - "↴": [t: "⠫⠠⠳⠒⠒⠕"] # 0x21b4 - - "↵": [t: "⠫⠩⠠⠳⠒⠒⠕"] # 0x21b5 - - "↶": [t: "⠫⠢⠔⠕"] # 0x21b6 - - "↷": [t: "⠫⠪⠢⠔"] # 0x21b7 - - "↺": [t: "⠫⠢⠔⠕"] # 0x21ba - - "↻": [t: "⠫⠪⠢⠔"] # 0x21bb - - "⇄": [t: "⠫⠒⠒⠕⠫⠪⠒⠒"] # 0x21c4 - - "⇅": [t: "⠫⠣⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21c5 - - "⇆": [t: "⠫⠪⠒⠒⠫⠒⠒⠕"] # 0x21c6 - - "⇇": [t: "⠫⠚⠒⠒⠫⠚⠒⠒"] # 0x21c7 - - "⇈": [t: "⠫⠣⠒⠒⠕⠐⠫⠣⠒⠒⠕"] # 0x21c8 - - "⇉": [t: "⠫⠒⠒⠕⠫⠒⠒⠕"] # 0x21c9 - - "⇊": [t: "⠫⠩⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21ca - - "⇍": [t: "⠳⠈⠫⠪⠪⠒⠒"] # 0x21cd - - "⇎": [t: "⠳⠈⠫⠪⠪⠒⠒⠕⠕"] # 0x21ce - - "⇏": [t: "⠳⠈⠫⠒⠒⠕⠕"] # 0x21cf - - "⇐": [t: "⠫⠪⠶⠶"] # 0x21d0 - - "⇑": [t: "⠫⠣⠶⠶⠕"] # 0x21d1 - - "⇒": [t: "⠫⠶⠶⠕"] # 0x21d2 - - "⇓": [t: "⠫⠣⠶⠶⠕"] # 0x21d3 - - "⇔": [t: "⠫⠪⠶⠶⠕"] # 0x21d4 - - "⇕": [t: "⠫⠣⠪⠶⠶⠕"] # 0x21d5 - - "⇖": [t: "⠫⠘⠪⠶⠶"] # 0x21d6 - - "⇗": [t: "⠫⠘⠶⠶⠕"] # 0x21d7 - - "⇘": [t: "⠫⠰⠶⠶⠕"] # 0x21d8 - - "⇙": [t: "⠫⠰⠪⠪⠒⠒"] # 0x21d9 - - "⇚": [t: "⠫⠪⠪⠶⠶"] # 0x21da FIX: how to represent triple shafts? - - "⇛": [t: "⠫⠶⠶⠕⠕"] # 0x21db FIX: how to represent triple shafts? - - "⇜": [t: "⠫⠪⠢⠤⠔⠒⠢"] # 0x21dc - - "⇝": [t: "⠫⠢⠤⠔⠒⠢⠕"] # 0x21dd - - "⇞": [t: "⠳⠳⠈⠫⠣⠒⠒⠕⠻"] # 0x21de - - "⇟": [t: "⠳⠳⠈⠫⠩⠒⠒⠕⠻"] # 0x21df - - "⇠": [t: "⠫⠪⠒⠒"] # 0x21e0 - - "⇡": [t: "⠫⠣⠒⠒⠕"] # 0x21e1 - - "⇢": [t: "⠫⠒⠒⠕"] # 0x21e2 - - "⇣": [t: "⠫⠩⠒⠒⠕"] # 0x21e3 - - "⇤": [t: "⠫⠳⠪⠒⠒"] # 0x21e4 - - "⇥": [t: "⠫⠒⠒⠕⠳"] # 0x21e5 - - "⇦": [t: "⠫⠸⠪⠒⠒"] # 0x21e6 - - "⇧": [t: "⠫⠣⠸⠒⠒⠕"] # 0x21e7 - - "⇨": [t: "⠫⠸⠒⠒⠕"] # 0x21e8 - - "⇩": [t: "⠫⠩⠸⠒⠒⠕"] # 0x21e9 - - "⇳": [t: "⠫⠣⠸⠪⠒⠒⠕"] # 0x21f3 - - "⇴": [t: "⠫⠒⠒⠕⠨⠡"] # 0x21f4 - - "⇵": [t: "⠫⠩⠒⠒⠕⠐⠫⠣⠒⠒⠕"] # 0x21f5 - - "⇶": [t: "⠫⠒⠒⠕⠫⠒⠒⠕⠫⠒⠒⠕"] # 0x21f6 - - "⇷": [t: "⠳⠈⠫⠪⠒⠒⠻"] # 0x21f7 - - "⇸": [t: "⠳⠈⠫⠒⠒⠕⠻"] # 0x21f8 - - "⇹": [t: "⠳⠈⠫⠪⠒⠒⠕"] # 0x21f9 - - "⇺": [t: "⠳⠳⠈⠫⠪⠒⠒⠻"] # 0x21fa - - "⇻": [t: "⠳⠳⠈⠫⠒⠒⠕⠻"] # 0x21fb - - "⇼": [t: "⠳⠳⠈⠫⠪⠒⠒⠕"] # 0x21fc - - "⇽": [t: "⠫⠳⠒⠒"] # 0x21fd - - "⇾": [t: "⠫⠒⠒⠳"] # 0x21fe - - "⇿": [t: "⠫⠳⠒⠒⠳"] # 0x21ff - - - "ⅆ": [t: "⠙"] # 0x2146 - - "ⅇ": [t: "⠑"] # 0x2147 - - "ⅈ": [t: "⠊"] # 0x2148 - - "⌜": [t: "⠈⠘⠷"] # 0x231c - - "⌝": [t: "⠈⠘⠾"] # 0x231d - - "⌞": [t: "⠈⠰⠷"] # 0x231e - - "⌟": [t: "⠈⠰⠾"] # 0x231f - - "〈": [t: "⠨⠨⠷"] # 0x2329 - - "〉": [t: "⠨⠨⠾"] # 0x232a - - "⏞": [t: "⠨⠷"] # 0x23de - - "⏟": [t: "⠨⠾"] # 0x23df - - "⎴": [t: "⠈⠷"] # 0x23b4 - - "⎵": [t: "⠈⠾"] # 0x23b5 - - "⟪": [t: "⠨⠨⠨⠷"] # 0x27ea - - "⟫": [t: "⠨⠨⠨⠾"] # 0x27eb - - "〈": [t: "⠨⠨⠷"] # 0x3008 - - "〉": [t: "⠨⠨⠾"] # 0x3009 - - "《": [t: "⠨⠨⠨⠷"] # 0x300a - - "》": [t: "⠨⠨⠨⠾"] # 0x300b - - "「": [t: "⠈⠘⠷"] # 0x300c - - "」": [t: "⠈⠘⠾"] # 0x300d - - "↼": [t: "⠫⠈⠪⠒⠒"] # 0x21bc - - "↽": [t: "⠫⠠⠪⠒⠒"] # 0x21bd - - "↾": [t: "⠫⠣⠒⠒⠠⠕"] # 0x21be - - "↿": [t: "⠫⠣⠒⠒⠈⠕"] # 0x21bf - - "⇀": [t: "⠫⠒⠒⠈⠕"] # 0x21c0 - - "⇁": [t: "⠫⠒⠒⠠⠕"] # 0x21c1 - - "⇂": [t: "⠫⠪⠒⠒⠈⠕"] # 0x21c2 - - "⇃": [t: "⠫⠩⠒⠒⠠⠕"] # 0x21c3 - - "⇋": # 0x21cb - - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠒⠈⠕"] - - "⇌": # 0x21cc - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - - "℔": [t: "⠳"] # 0x2114 - - "™": [t: "⠘⠞"] # 0x2122 - - "Å": [t: "⠈⠠⠁"] # 0x212b - - "­": [t: "⠤"] # 0xad - - " -​": [t: "W"] # 0x2000 - 0x200b (various spaces / ZWSP) - - "
": [t: "W"] # 0x2028 - - "
": [t: "W"] # 0x2029 - - " ": [t: "W"] # 0x202f - - " ": [t: "W"] # 0x205f - - "₣": [t: "⠈⠋"] # 0x20A3 (French franc sign - - "₦": [t: "⠈⠝"] # 0x2046 (Naira sign) - - "₩": [t: "⠈⠺"] # 0x20A9 (Won sign) - - "€": [t: "⠈⠑"] # 0x20AC (Euro sign) - - "": [t: "W"] # 0xfeff - - "☆": [t: "⠫⠎"] # 0x2606 - - "✕": [t: "⠈⠡"] # 0x2715 - - "¼": [t: "⠹⠂⠌⠲⠼"] # 0xbc - - "½": [t: "⠹⠂⠌⠆⠼"] # 0xbd - - "¾": [t: "⠹⠒⠌⠲⠼"] # 0xbe - - "⅐": [t: "⠹⠂⠌⠶⠼"] # 0x2150 - - "⅑": [t: "⠹⠂⠌⠔⠼"] # 0x2151 - - "⅒": [t: "⠹⠂⠌⠂⠴⠼"] # 0x2152 - - "⅓": [t: "⠹⠂⠌⠒⠼"] # 0x2153 - - "⅔": [t: "⠹⠆⠌⠒⠼"] # 0x2154 - - "⅕": [t: "⠹⠂⠌⠢⠼"] # 0x2155 - - "⅖": [t: "⠹⠆⠌⠢⠼"] # 0x2156 - - "⅗": [t: "⠹⠒⠌⠢⠼"] # 0x2157 - - "⅘": [t: "⠹⠲⠌⠢⠼"] # 0x2158 - - "⅙": [t: "⠹⠂⠌⠖⠼"] # 0x2159 - - "⅚": [t: "⠹⠢⠌⠖⠼"] # 0x215a - - "⅛": [t: "⠹⠂⠌⠦⠼"] # 0x215b - - "⅜": [t: "⠹⠒⠌⠦⠼"] # 0x215c - - "⅝": [t: "⠹⠢⠌⠦⠼"] # 0x215d - - "⅞": [t: "⠹⠶⠌⠦⠼"] # 0x215e - - "⅟": [t: "⠹⠂⠌⠼"] # 0x215f - - "↉": [t: "⠹⠴⠌⠒⠼"] # 0x2189 - - "㉈": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x3248 - - "㉉": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠆⠴⠻⠻"] # 0x3249 - - "㉊": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠒⠴⠻⠻"] # 0x324a - - "㉋": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠲⠴⠻⠻"] # 0x324b - - "㉌": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠢⠴⠻⠻"] # 0x324c - - "㉍": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠖⠴⠻⠻"] # 0x324d - - "㉎": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠶⠴⠻⠻"] # 0x324e - - "㉏": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠦⠴⠻⠻"] # 0x324f - - - - "‘": [t: "P⠠⠦"] # 0x2018 (Left single quotation mark) - - "’": [t: "P⠠⠴"] # 0x2019 (Right single quotation mark) - - "“": [t: "P⠦"] # 0x201C (Left double quotation mark) - - "”": [t: "P⠴"] # 0x201D (Right double quotation mark) - - "‰": [t: "`00"] - - "ℏ": [t: "⠈⠓"] # 0x210F (Planck constant over two pi) - - "ℓ": [t: "TE⠇"] # 0x2113 (Script small l (differs from 1d4c1: 4-56-123)) - - "℘": [tc: "TCL⠏"] # 0x2118 (Script Capital P) -# - "ℝ": [t: ""] # 0x211D (Double-struck capital R (no nemeth double struck)) -# - "ⅆ": [t: ""] # 0x2146 (Double-struck italic small d) -# - "ⅇ": [t: ""] # 0x2147 (Double-struck italic small e) -# - "⇚": [t: "⠀⠫⠪⠸⠸⠀"] # 0x21DA (Leftwards triple arrow) -# - "⇛": [t: "⠀⠫⠸⠸⠕⠀"] # 0x21DB (Rightwards triple arrow) - - "∔": [t: "⠐⠬⠣⠡⠻"] # 0x2214 (Dot plus) - - "√": [t: "⠜"] # 0x221A (Square root) - - "∛": [t: "⠣⠒⠜"] # 0x221B (Cube root) - - "∜": [t: "⠣⠲⠜"] # 0x221C (Fourth root) - - "∡": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2221 (Measured angle) - - "∹": [t: "⠤⠐⠂"] # 0x2239 (Excess) - - "∺": [t: "⠐⠤⠩⠡⠡⠣⠡⠡⠻"] # 0x223A (Geometric proportion) - - "∻": [t: "⠐⠈⠱⠩⠡⠣⠡⠻"] # 0x223B (Homothetic) - - "∽": [t: "⠠⠱"] # 0x223D (Reversed tilde) - - "≋": [t: "⠈⠱⠈⠱⠈⠱"] # 0x224B (Triple tilde) - - "≌": [t: "⠠⠱⠨⠅"] # 0x224C (All equal to) - - "≏": [t: "⠈⠣⠱"] # 0x224F (Difference between) - - "≔": [t: "⠐⠂⠨⠅"] # 0x2254 (Colon equals) - - "≕": [t: "⠨⠅⠐⠂"] # 0x2255 (Equals colon) - - "≖": [t: "⠀⠨⠡⠈⠨⠅⠻⠀"] # 0x2256 (Ring in equal to) - - "⊌": [t: "⠨⠬⠈⠫⠪⠒⠻"] # 0x228C (Multiset) - - "⊍": [t: "⠡⠈⠨⠬⠻"] # 0x228D (Multiset multiplication) - - "⊎": [t: "⠬⠈⠨⠬⠻"] # 0x228E (Multiset union) - - "⊕": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2295 (Circled plus) - - "⊖": [t: "⠫⠉⠸⠫⠤⠻"] # 0x2296 (Circled minus) - - "⊗": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2297 (Circled times) - - "⊘": [t: "⠫⠉⠸⠫⠸⠌⠻"] # 0x2298 (Circled division slash) - - "⊙": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2299 (Circled dot operator) - - "⊚": [t: "⠫⠉⠸⠫⠘⠨⠡⠻"] # 0x229a (Circled ring operator) - - "⊛": [t: "⠫⠉⠸⠫⠈⠼⠻"] # 0x229B (Circled asterisk operator) - - "⊜": [t: "⠫⠉⠸⠫⠨⠅⠻"] # 0x229C (Circled equals) - - "⊝": [t: "⠫⠉⠸⠫⠤⠤⠻"] # 0x229D (Circled dash) - - "⊞": [t: "⠫⠲⠸⠫⠬⠻"] # 0x229E (Squared plus) - - "⊟": [t: "⠫⠲⠸⠫⠤⠻"] # 0x229F (Squared minus) - - "⊠": [t: "⠫⠲⠸⠫⠈⠡⠻"] # 0x22A0 (Squared times) - - "⊡": [t: "⠫⠲⠸⠫⠡⠻"] # 0x22A1 (Squared dot operator) - - "⊢": [t: "⠫⠳⠒"] # 0x22A2 (Right tack) - - "⊣": [t: "⠫⠒⠳"] # 0x22A3 (Left tack) - - "⊤": [t: "⠫⠩⠳⠒"] # 0x22A4 (Down tack) -# - "⊥": [t: "⠀⠫⠏⠀ or ⠀⠫⠣⠳⠒⠀"] # 0x22A5 (Up tack (See also 27C2)) -# - "⊦": [t: ""] # 0x22A6 (Assertion) -# - "⊨": [t: ""] # 0x22A8 (TRUE) -# - "⊲": [t: ""] # 0x22B2 (Normal subgroup of) -# - "⊳": [t: ""] # 0x22B3 (Contains as normal subgroup) -# - "⊴": [t: ""] # 0x22B4 (Normal subgroup of or equal to) -# - "⊵": [t: ""] # 0x22B5 (Contains as normal subgroup or equal to) - - "⊶": [t: "⠫⠨⠡⠒⠡"] # 0x22B6 (Original of) - - "⊷": [t: "⠫⠡⠒⠨⠡"] # 0x22B7 (Image of) - - "⊸": [t: "⠫⠒⠨⠡"] # 0x22B8 (Multimap) -# - "⊾": [t: ""] # 0x22BE (Right angle w arc) - - "⊿": [t: "⠫⠞⠨⠗⠻"] # 0x22BF (Right triangle) - - "⋅": [t: "⠡"] # 0x22C5 (Dot operator) - - "∙": [t: "⠡"] # 0x2219 -- similar looking to 0x22c5, so using the same braille - - "⋆": [t: "⠫⠎"] # 0x22C6 (Star operator) - - "⋍": [t: "⠠⠱⠱"] # 0x22CD (Reversed tilde equals) - - "⋖": [t: "⠡⠈⠐⠅⠻"] # 0x22D6 (Less-than w dot) - - "⋗": [t: "⠡⠈⠨⠂⠻"] # 0x22D7 (Greater-than w dot) - - "⋘": [t: "⠐⠅⠈⠐⠅⠈⠐⠅⠻"] # 0x22D8 (Much less-than) - - "⋙": [t: "⠨⠂⠈⠨⠂⠈⠨⠂⠻"] # 0x22D9 (Much greater-than) - - "⋲": [t: "⠱⠈⠈⠑⠻"] # 0x22F2 (Element of w long horizontal stroke) - - "⋵": [t: "⠐⠈⠑⠣⠡⠻"] # 0x22F5 (Element of w dot above) - - "⋶": [t: "⠱⠈⠑"] # 0x22F6 (Element of w overbar) - - "⋺": [t: "⠱⠈⠈⠢⠻"] # 0x22FA (Contains w long horizontal stroke) - - "⋽": [t: "⠱⠈⠢"] # 0x22FD (Contains w overbar) - - "⌈": [t: "⠈⠘⠷"] # 0x2308 (Left ceiling) - - "⌉": [t: "⠈⠘⠾"] # 0x2309 (Right ceiling) - - "⌊": [t: "⠈⠰⠷"] # 0x230A (Left floor) - - "⌋": [t: "⠈⠰⠾"] # 0x230B (Right floor) - - "⌢": [t: "⠫⠁"] # 0x2322 (Frown) - - "⌣": [t: "⠫⠄"] # 0x2323 (Smile) - - "■": [t: "⠫⠸⠲"] # 0x25A0 (Filled square) - - "▫": [t: "⠫⠲"] # 0x25AB (Small white Square) - - "□": [t: "⠫⠲"] # 0x25A1 (Square) - - "◻": [t: "⠫⠲"] # 0x25FB (Square) - - "▬": [t: "⠫⠸⠗"] # 0x25AC (Filled rectangle) - - "▭": [t: "⠫⠗"] # 0x25AD (Rectangle) - - "▲": [t: "⠫⠸⠞"] # 0x25B2 (Black up-pointing triangle) - - "△": [t: "⠫⠞"] # 0x25B3 (White up-pointing triangle) - - "▼": [t: "⠸⠨⠫"] # 0x25BC (Black down-pointing triangle) - - "○": [t: "⠫⠉"] # 0x25CB (White circle) - - "●": [t: "⠫⠸⠉"] # 0x25CF (Black circle) - - "◫": [t: "⠫⠲⠸⠫⠳⠻"] # 0x25EB (White square w vertical bisecting line) -# - "⊥": [t: "⠫⠏"] # 0x27C2 (Perpendicular) - - "⟃": [t: "⠨⠡⠈⠸⠐⠅⠻"] # 0x27C3 (Open subset) - - "⟄": [t: "⠨⠡⠈⠸⠨⠂⠻"] # 0x27C4 (Open superset) - - "⟜": [t: "⠫⠨⠡⠒⠒"] # 0x27DC (Left multimap) - - "⟝": [t: "⠫⠳⠒⠒"] # 0x27DD (Long right tack) - - "⟞": [t: "⠫⠒⠒⠳"] # 0x27DE (Long left tack) - - "⟟": [t: "⠫⠣⠳⠒⠒⠨⠡"] # 0x27DF (Up tack w circle above) - - "⟦": [t: "⠈⠸⠷"] # 0x27E6 (Mathematical left white square bracket) - - "⟧": [t: "⠈⠸⠾"] # 0x27E7 (Mathematical right white square bracket) - - "⟨": [t: "⠨⠨⠷"] # 0x27E8 (Mathematical left angle bracket) - - "⟩": [t: "⠨⠨⠾"] # 0x27E9 (Mathematical right angle bracket) - - "⟵": [t: "⠀⠫⠪⠒⠒⠒⠀"] # 0x27F5 (Long leftwards arrow ) - - "⟶": [t: "⠀⠫⠒⠒⠒⠕⠀"] # 0x27F6 (Long rightwards arrow) - - "⟷": [t: "⠀⠫⠪⠒⠒⠒⠕⠀"] # 0x27F7 (Long left right arrow) - - "⟸": [t: "⠀⠫⠪⠶⠶⠶⠀"] # 0x27F8 (Long leftwards arrow ) - - "⟹": [t: "⠀⠫⠶⠶⠶⠕⠀"] # 0x27F9 (Long rightwards arrow) - - "⟺": [t: "⠀⠫⠪⠶⠶⠶⠕⠀"] # 0x27FA (Long left right arrow) - - "⟻": [t: "⠀⠫⠪⠒⠒⠒⠳⠀"] # 0x27FB (Long leftwards arrow from bar) - - "⟼": [t: "⠀⠫⠳⠒⠒⠒⠕⠀"] # 0x27FC (Long rightwards arrow from bar) - - "⟽": [t: "⠀⠫⠪⠶⠶⠶⠳⠀"] # 0x27FD (Long leftwards double arrow from bar) - - "⟾": [t: "⠀⠫⠳⠶⠶⠶⠕⠀"] # 0x27FE (Long rightwards double arrow from bar) - - "⟿": [t: "⠀⠫⠢⠤⠔⠒⠢⠤⠔⠒⠢⠕⠀"] # 0x27FF (Long rightwards squiggle arrow) - - "⤡": [t: "⠀⠫⠘⠪⠒⠒⠕⠀"] # 0x2921 (North west and south east arrow) - - "⤢": [t: "⠀⠫⠰⠪⠒⠒⠕⠀"] # 0x2922 (North east and south west arrow) - - "🣐": # 0x1f8d0 (Long rightwards arrow over leftwards arrow (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣑": # 0x1f8d1 (Long rightwards harpoon over leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣒": # 0x1f8d2 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - "🣓": # 0x1f8d3 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒⠒"] - else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣔": # 0x1f8d4 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] - - "🣕": # 0x1f8d5 (Short leftwards harpoon over long rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠫⠒⠒⠕"] - else: [t: "⠫⠈⠪⠒⠫⠒⠒⠠⠕"] - - "⥂": # 0x2942 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - "⥄": # 0x2944 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒⠒"] - else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] - - "⥃": # 0x2943 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] - - "🣖": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d6 (long rightwards arrow with through X) - - "🣗": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d7 (long rightwards arrow with double slash) chem code says it is same as 0x1f8d6 - - - - "⦃": [t: "⠨⠸⠷"] # 0x2983 (Left white curly bracket) - - "⦄": [t: "⠨⠸⠾"] # 0x2984 (Right white curly bracket) - - "⦑": [t: "⠡⠈⠨⠨⠷⠻"] # 0x2991 (Left angle bracket w dot) - - "⦒": [t: "⠡⠈⠨⠨⠾⠻"] # 0x2992 (Right angle bracket w dot) - - "⧄": [t: "⠫⠲⠸⠫⠔⠻"] # 0x29C4 (Square rising diagonal slash) - - "⧅": [t: "⠫⠲⠸⠫⠢⠻"] # 0x29C5 (Square falling diagonal slash) - - "⧆": [t: "⠫⠲⠸⠫⠈⠼⠻"] # 0x29C6 (Squared asterisk) - - "⧊": [t: "⠐⠫⠞⠣⠡⠻"] # 0x29CA (Triangle w dot above) - - "⧋": [t: "⠫⠞⠱"] # 0x29CB (Triangle w underbar) - - "⧌": [t: "⠫⠞⠎"] # 0x29CC (s in triangle §114) - - "⧦": [t: "⠫⠳⠶⠶⠳"] # 0x29E6 (Gleich stark) - - "⨌": [t: "⠮⠮⠮⠮"] # 0x2A0C (Quadruple integral operator) - - "⨍": [t: "⠮⠈⠱⠻"] # 0x2A0D (Finite part integral) - - "⨎": [t: "⠮⠈⠱⠱⠻"] # 0x2A0E (Integral w double stroke) - - "⨖": [t: "⠮⠈⠫⠲⠻"] # 0x2A16 (Quaternion integral operator) - - "⨘": [t: "⠮⠈⠈⠡⠻"] # 0x2A18 (Integral w times sign) - - "⨙": [t: "⠮⠈⠨⠩⠻"] # 0x2A19 (Integral w intersection) - - "⨚": [t: "⠮⠈⠨⠬⠻"] # 0x2A1A (Integral w union) - - "⨛": [t: "⠣⠮"] # 0x2A1B (Integral w overbar (upper)) - - "⨜": [t: "⠩⠮"] # 0x2A1C (Integral w underbar (lower)) - - "⨢": [t: "⠐⠬⠣⠨⠡⠻"] # 0x2A22 (Plus sign w small circle above) - - "⨣": [t: "⠐⠬⠣⠸⠣⠻"] # 0x2A23 (Plus sign w circumflex accent above) - - "⨤": [t: "⠐⠬⠣⠈⠱⠻"] # 0x2A24 (Plus sign w tilde above) - - "⨥": [t: "⠐⠬⠩⠡⠻"] # 0x2A25 (Plus sign w dot below) - - "⨪": [t: "⠐⠱⠩⠡⠻"] # 0x2A2A (Minus sign w dot below) - - "⨰": [t: "⠐⠈⠡⠣⠡⠻"] # 0x2A30 (Multiplication sign w dot above) - - "⨱": [t: "⠈⠡⠱"] # 0x2A31 (Multiplication sign w underbar) - - "⨸": [t: "⠫⠉⠸⠫⠨⠌⠻"] # 0x2A38 (Circled division sign) - - "⨹": [t: "⠫⠞⠸⠫⠬⠻"] # 0x2A39 (Plus sign in triangle) - - "⨺": [t: "⠫⠞⠸⠫⠤⠻"] # 0x2A3A (Minus sign in triangle) - - "⨻": [t: "⠫⠞⠸⠫⠈⠡⠻"] # 0x2A3B (Multiplication sign in triangle) - - "⩀": [t: "⠡⠈⠨⠩⠻"] # 0x2A40 (Intersection w dot) - - "⩁": [t: "⠤⠈⠨⠬⠻"] # 0x2A41 (Union w minus sign) - - "⩂": [t: "⠱⠨⠬"] # 0x2A42 (Union w overbar) - - "⩃": [t: "⠱⠨⠩"] # 0x2A43 (Intersection w overbar) - - "⩑": [t: "⠐⠈⠩⠣⠡⠻"] # 0x2A51 (Logical AND w dot above) - - "⩒": [t: "⠐⠈⠬⠣⠡⠻"] # 0x2A52 (Logical OR w dot above) - - "⩜": [t: "⠱⠈⠈⠩⠻"] # 0x2A5C (Logical AND with horizontal dash) - - "⩝": [t: "⠱⠈⠈⠬⠻"] # 0x2A5D (Logical OR with horizontal dash) - - "⩞": [t: "⠱⠱⠈⠩"] # 0x2A5E (Logical AND w double overbar) - - "⩟": [t: "⠈⠩⠱"] # 0x2A5F (Logical AND w underbar) - - "⩠": [t: "⠈⠩⠱⠱"] # 0x2A60 (Logical AND w double underbar) - - "⩢": [t: "⠱⠱⠈⠬"] # 0x2A62 (Logical OR w double overbar) - - "⩣": [t: "⠈⠬⠱⠱"] # 0x2A63 (Logical OR w double underbar) - - "⩦": [t: "⠐⠨⠅⠩⠡⠻"] # 0x2A66 (Equals sign w dot below) - - "⩧": [t: "⠐⠸⠇⠣⠡⠻"] # 0x2A67 (Identical w dot above) - - "⩪": [t: "⠐⠈⠱⠣⠡⠻"] # 0x2A6A (Tilde operator w dot above) - - "⩬": [t: "⠈⠱⠱⠈⠱"] # 0x2A6C (Similar minus similar) - - "⩭": [t: "⠐⠈⠱⠨⠅⠣⠡⠻"] # 0x2A6D (Congruent w dot above) - - "⩮": [t: "⠐⠨⠅⠣⠈⠼⠻"] # 0x2A6E (Equals w asterisk) - - "⩯": [t: "⠐⠈⠱⠈⠱⠣⠸⠣⠻"] # 0x2A6F (Almost equal to w circumflex) - - "⩰": [t: "⠈⠱⠈⠱⠨⠅"] # 0x2A70 (Approximately equal or equal to) - - "⩱": [t: "⠨⠅⠬"] # 0x2A71 (Equals sign above plus sign) - - "⩲": [t: "⠬⠨⠅"] # 0x2A72 (Plus sign above equals sign) - - "⩳": [t: "⠨⠅⠈⠱"] # 0x2A73 (Equals sign above tilde operator) - - "⩴": [t: "⠐⠂⠐⠂⠨⠅"] # 0x2A74 (Double colon equal) - - "⩵": [t: "⠨⠅⠨⠅"] # 0x2A75 (Two consecutive equals signs) - - "⩶": [t: "⠨⠅⠨⠅⠨⠅"] # 0x2A76 (Three consecutive equals signs) - - "⩷": [t: "⠐⠨⠅⠩⠡⠡⠣⠡⠡⠻"] # 0x2A77 (Equals sign w two dots above and two dots below) - - "⩸": [t: "⠐⠸⠇⠣⠡⠡⠡⠡⠻"] # 0x2A78 (Equivalent w four dots above) - - "⩹": [t: "⠐⠅⠈⠨⠡⠻"] # 0x2A79 (Less than w circle inside) - - "⩺": [t: "⠨⠂⠈⠨⠡⠻"] # 0x2A7A (Greater than w circle inside) - - "⩻": [t: "⠐⠐⠅⠣⠸⠦⠻"] # 0x2A7B (Less-than w question mark above) - - "⩼": [t: "⠐⠨⠂⠣⠸⠦⠻"] # 0x2A7C (Greater-than w question mark above) - - "⪅": [t: "⠐⠅⠈⠱⠈⠱"] # 0x2A85 (Less-than or approximate) - - "⪆": [t: "⠨⠂⠈⠱⠈⠱"] # 0x2A86 (Greater-than or approximate) - - "⪋": [t: "⠐⠅⠨⠅⠨⠂"] # 0x2A8B (Less-than above double-line equal above greater-than) - - "⪌": [t: "⠨⠂⠨⠅⠐⠅"] # 0x2A8C (Greater-than above double-line equal above less-than) - - "⪍": [t: "⠐⠅⠈⠱⠱"] # 0x2A8D (Less-than above similar or equal to) - - "⪎": [t: "⠨⠂⠈⠱⠱"] # 0x2A8E (Greater-than above similar or equal to) - - "⪏": [t: "⠐⠅⠈⠱⠨⠂"] # 0x2A8F (Less-than above similar above greater-than) - - "⪐": [t: "⠨⠂⠈⠱⠐⠅"] # 0x2A90 (Greater-than above similar above less-than) - - "⪑": [t: "⠐⠅⠨⠂⠨⠅"] # 0x2A91 (Less-than above greater-than above double-line equal) - - "⪒": [t: "⠨⠂⠐⠅⠨⠅"] # 0x2A92 (Greater-than above less-than above double-line equal) - - "⪙": [t: "⠨⠅⠐⠅"] # 0x2A99 (Double-line equal to or less-than) - - "⪚": [t: "⠨⠅⠨⠂"] # 0x2A9A (Double-line equal to or greater-than) - - "⪝": [t: "⠈⠱⠐⠅"] # 0x2A9D (Similar or less-than) - - "⪞": [t: "⠈⠱⠨⠂"] # 0x2A9E (Similar or greater-than) - - "⪟": [t: "⠈⠱⠐⠅⠨⠅"] # 0x2A9F (Similar above less-than above equals sign) - - "⪠": [t: "⠈⠱⠨⠂⠨⠅"] # 0x2AA0 (Similar above greater-than above equals sign) - - "⪮": [t: "⠈⠣⠨⠅"] # 0x2AAE (Equals sign with bumpy above) - - "⪽": [t: "⠡⠈⠸⠐⠅⠻"] # 0x2ABD (Subset w dot) - - "⪾": [t: "⠡⠈⠸⠨⠂⠻"] # 0x2ABE (Superset w dot) - - "⪿": [t: "⠐⠸⠐⠅⠩⠬⠻"] # 0x2ABF (Subset with plus sign below) - - "⫀": [t: "⠐⠸⠨⠂⠩⠬⠻"] # 0x2AC0 (Superset with plus sign below) - - "⫁": [t: "⠐⠸⠐⠅⠩⠈⠡⠻"] # 0x2AC1 (Subset with multiplication sign below) - - "⫂": [t: "⠐⠸⠨⠂⠩⠈⠡⠻"] # 0x2AC2 (Superset with multiplication sign below) - - "⫅": [t: "⠸⠐⠅⠨⠅"] # 0x2AC5 (Subset of above equals sign) - - "⫆": [t: "⠸⠨⠂⠨⠅"] # 0x2AC6 (Superset of above equals sign) - - "⫇": [t: "⠸⠐⠅⠈⠱"] # 0x2AC7 (Subset of above tilde operator) - - "⫈": [t: "⠸⠨⠂⠈⠱"] # 0x2AC8 (Superset of above tilde operator) - - "⫉": [t: "⠸⠐⠅⠈⠱⠈⠱"] # 0x2AC9 (Subset of above almost equal to) - - "⫊": [t: "⠸⠨⠂⠈⠱⠈⠱"] # 0x2ACA (Superset of above almost equal to) - - "⫋": [t: "⠸⠐⠅⠌⠨⠅"] # 0x2ACB (Subset of above not equals sign) - - "⫌": [t: "⠸⠨⠂⠌⠨⠅"] # 0x2ACC (Superset of above not equals sign) - - "⫓": [t: "⠸⠐⠅⠸⠨⠂"] # 0x2AD3 (Subset above superset) - - "⫔": [t: "⠸⠨⠂⠸⠐⠅"] # 0x2AD4 (Superset above subset) - - "⫕": [t: "⠸⠐⠅⠸⠐⠅"] # 0x2AD5 (Subset above subset) - - "⫖": [t: "⠸⠨⠂⠸⠨⠂"] # 0x2AD6 (Superset above superset) - - "⫗": [t: "⠸⠨⠂⠐⠸⠐⠅"] # 0x2AD7 (Superset beside subset) - - "⫯": [t: "⠫⠩⠨⠡⠒⠒"] # 0x2AEF (Vertical line w circle above) - - "⫰": [t: "⠫⠣⠨⠡⠒⠒"] # 0x2AF0 (Vertical line w circle below) - - "⫱": [t: "⠫⠣⠨⠡⠒⠒⠳"] # 0x2AF1 (Down tack w circle below) - - "⫲": [t: "⠱⠈⠫⠇⠻"] # 0x2AF2 (Parallel w horizontal stroke) - - "⫳": [t: "⠈⠱⠈⠫⠇⠻"] # 0x2AF3 (Parallel w tilde operator) - - "⫴": [t: "⠳⠳⠳"] # 0x2AF4 (Triple vertical bar binary relation) - - "⬬": [t: "⠫⠸⠑"] # 0x2B2C (Black ellipse) - - "⬭": [t: "⠫⠑"] # 0x2B2D (White ellipse) - - "⬰": [t: "⠨⠡⠈⠫⠪⠒⠒⠻"] # 0x2B30 (Left arrow w small circle) - - "⬱": [t: "⠫⠪⠒⠒⠫⠪⠒⠒⠫⠪⠒⠒"] # 0x2B31 (Three leftwards arrows) - - "⬲": [t: "⠫⠪⠒⠒⠈⠫⠉⠸⠫⠬⠻"] # 0x2B32 (Left arrow w circled plus) - - "⮖": [t: "⠨⠡"] # 0x2B96 (Standard State Symbol) - - "⯃": [t: "⠫⠸⠦"] # 0x2BC3 (Horizontal black octagon) - - # SRE lists this as a custom transcription - - "⋊": [t: "⠈⠡⠳"] # 0x22ca (RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT) +--- +# To greatly simplify typeface/language generation, the chars have unique ASCII chars for them: +# Typeface: S: sans-serif, B: bold, T: script/blackboard, I: italic, R: Roman +# Language: E: English, D: German, G: Greek, V: Greek variants, H: Hebrew, U: Russian +# Indicators: C: capital, L: letter, N: number, P: punctuation, M: multipurpose +# Others: W -- whitespace that should be kept (e.g, in a numeral) +# SRE doesn't have H: Hebrew, so not encoded (yet) +# Some of the weird letters (e.g., circled letters) I didn't translate with the above because +# they don't interact with rules since they are enclosed in other chars. + + - "⨯": [t: "⠈⠡"] # U+2A2F(VECTOR OR CROSS PRODUCT) -- make the same as 0x00D7 (Multiplication sign) + + # --- Nemeth Default Alphabet bold chars. --- + - "𝐀-𝐙": # 0x1d400 - 0x1d419 + - tc: "BE" + - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝐚-𝐳": # 0x1d41a - 0x1d433 + - tc: "BE" + - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 + - tc: "BG" + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝛂-𝛒": # 0x1d6c2 - 0x1d6d2 + - tc: "BG" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒', 'αβγδεζηθικλμνξοπρ')" + + - "𝛔-𝛖": # 0x1d6d4 - 0x1d6d6 + - tc: "BG" + - spell: "translate('.', '𝛔𝛕𝛖', 'στυ')" + + - "𝛘-𝛚": # 0x1d6d8 - 0x1d6da + - tc: "BG" + - spell: "translate('.', '𝛘𝛙𝛚', 'χψω')" + + - "𝛁": [tc: "BGL⠫"] # 0x1d6c1 + - "𝛓": [tc: "BVL⠎"] # 0x1d6d3 + - "𝛗": [tc: "BVL⠋"] # 0x1d6d7 + - "𝛛": [tc: "⠸⠈⠙"] # 0x1d6db + - "𝛜": [tc: "BGL⠑"] # 0x1d6dc + - "𝛝": [tc: "BVL⠹"] # 0x1d6dd + - "𝛞": [tc: "BGL⠅"] # 0x1d6de + - "𝛟": [tc: "BGL⠋"] # 0x1d6df + - "𝛠": [tc: "BGL⠗"] # 0x1d6e0 + - "𝛡": [tc: "BGL⠏"] # 0x1d6e1 + + - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 + - tc: "B" + - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + + # --- Nemeth Default Alphabet bold-fraktur chars. --- + + - "𝕬-𝖅": # 0x1d56c - 0x1d585 + - tc: "BD" + - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖆-𝖟": # 0x1d586 - 0x1d59f + - tc: "BD" + - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet bold-italic chars. --- + - "𝑨-𝒁": # 0x1d468 - 0x1d481 + - tc: "BE" + - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝒂-𝒛": # 0x1d482 - 0x1d49b + - tc: "BE" + - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝜜-𝜴": # 0x1d71c - 0x1d734 + - tc: "BG" + - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝜵": [tc: "BGL⠫"] # 0x1d735 + + - "𝜶-𝝆": # 0x1d736 - 0x1d746 + - tc: "BG" + - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆', 'αβγδεζηθικλμνξοπρ')" + + - "𝝇": [tc: "BVL⠎"] # 0x1d747 + + - "𝝈-𝝊": # 0x1d748 - 0x1d74a + - tc: "BG" + - spell: "translate('.', '𝝈𝝉𝝊', 'στυ')" + + - "𝝋": [tc: "BVL⠋"] # 0x1d74b + + - "𝝌-𝝎": # 0x1d74c - 0x1d74e + - tc: "BG" + - spell: "translate('.', '𝝌𝝍𝝎', 'χψω')" + + - "𝝏": [tc: "BVL⠙"] # 0x1d74f + - "𝝐": [tc: "BGL⠑"] # 0x1d750 + - "𝝑": [tc: "BVL⠹"] # 0x1d751 + + - "𝝒-𝝕": # 0x1d752 - 0x1d755 + - tc: "BG" + - spell: "translate('.', '𝝒𝝓𝝔𝝕', 'κφρπ')" + + # --- Nemeth Default Alphabet bold-script chars. --- + + - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 + - tc: "BTE" + - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝓪-𝔃": # 0x1d4ea - 0x1d503 + - tc: "BTE" + - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚤-𝚥": # 0x1d6a4 - 0x1d6a5 + - tc: "" + - spell: "translate('.', '𝚤𝚥', '⠊⠚')" + +# Russian Alphabet (from Nemeth book) +# NOTE: is this translation needed? + - "А-Я": # 0x410 - 0x42f + - tc: "U" + - spell: "translate('.', 'АБВГДЕЗИЙКЛМНОПРСТУФЭ', 'ABVGDEZIYKLMNOPRSTUFE')" + + - "а-я": # 0x430 - 0x44f + - tc: "U" + - spell: "translate('.', 'абвгдезийклмнопрстуфэ', 'abvgdeziylkmnoprstufye')" + + - "①-⑨": # 0x2460 - 0x2473 + - tc: "⠫⠉⠸⠫⠼" + - spell: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" + - tc: "⠻" + + - "⓪": [tc: "⠫⠉⠸⠫⠼⠴⠻"] # 0x24ea + + - "⑩": [tc: "⠫⠉⠸⠫⠼⠂⠴⠻"] # 0x2469 + - "⑪": [tc: "⠫⠉⠸⠫⠼⠂⠂⠻"] # 0x246a + - "⑫": [tc: "⠫⠉⠸⠫⠼⠂⠆⠻"] # 0x246b + - "⑬": [tc: "⠫⠉⠸⠫⠼⠂⠒⠻"] # 0x246c + - "⑭": [tc: "⠫⠉⠸⠫⠼⠂⠲⠻"] # 0x246d + - "⑮": [tc: "⠫⠉⠸⠫⠼⠂⠢⠻"] # 0x246e + - "⑯": [tc: "⠫⠉⠸⠫⠼⠂⠖⠻"] # 0x246f + - "⑰": [tc: "⠫⠉⠸⠫⠼⠂⠶⠻"] # 0x2470 + - "⑱": [tc: "⠫⠉⠸⠫⠼⠂⠦⠻"] # 0x2471 + - "⑲": [tc: "⠫⠉⠸⠫⠼⠂⠔⠻"] # 0x2472 + - "⑳": [tc: "⠫⠉⠸⠫⠼⠆⠴⠻"] # 0x2473 + + - "㉑": [tc: "⠫⠉⠸⠫⠼⠆⠂⠻"] # 0x3251 + - "㉒": [tc: "⠫⠉⠸⠫⠼⠆⠆⠻"] # 0x3252 + - "㉓": [tc: "⠫⠉⠸⠫⠼⠆⠒⠻"] # 0x3253 + - "㉔": [tc: "⠫⠉⠸⠫⠼⠆⠲⠻"] # 0x3254 + - "㉕": [tc: "⠫⠉⠸⠫⠼⠆⠢⠻"] # 0x3255 + - "㉖": [tc: "⠫⠉⠸⠫⠼⠆⠖⠻"] # 0x3256 + - "㉗": [tc: "⠫⠉⠸⠫⠼⠆⠶⠻"] # 0x3257 + - "㉘": [tc: "⠫⠉⠸⠫⠼⠆⠦⠻"] # 0x3258 + - "㉙": [tc: "⠫⠉⠸⠫⠼⠆⠔⠻"] # 0x3259 + - "㉚": [tc: "⠫⠉⠸⠫⠼⠒⠴⠻"] # 0x325a + - "㉛": [tc: "⠫⠉⠸⠫⠼⠒⠂⠻"] # 0x325b + - "㉜": [tc: "⠫⠉⠸⠫⠼⠒⠆⠻"] # 0x325c + - "㉝": [tc: "⠫⠉⠸⠫⠼⠒⠒⠻"] # 0x325d + - "㉞": [tc: "⠫⠉⠸⠫⠼⠒⠲⠻"] # 0x325e + - "㉟": [tc: "⠫⠉⠸⠫⠼⠒⠢⠻"] # 0x325f + - "㊱": [tc: "⠫⠉⠸⠫⠼⠒⠖⠻"] # 0x32b1 + - "㊲": [tc: "⠫⠉⠸⠫⠼⠒⠶⠻"] # 0x32b2 + - "㊳": [tc: "⠫⠉⠸⠫⠼⠒⠦⠻"] # 0x32b3 + - "㊴": [tc: "⠫⠉⠸⠫⠼⠒⠔⠻"] # 0x32b4 + - "㊵": [tc: "⠫⠉⠸⠫⠼⠲⠴⠻"] # 0x32b5 + - "㊶": [tc: "⠫⠉⠸⠫⠼⠲⠂⠻"] # 0x32b6 + - "㊷": [tc: "⠫⠉⠸⠫⠼⠲⠆⠻"] # 0x32b7 + - "㊸": [tc: "⠫⠉⠸⠫⠼⠲⠒⠻"] # 0x32b8 + - "㊹": [tc: "⠫⠉⠸⠫⠼⠲⠲⠻"] # 0x32b9 + - "㊺": [tc: "⠫⠉⠸⠫⠼⠲⠢⠻"] # 0x32ba + - "㊻": [tc: "⠫⠉⠸⠫⠼⠲⠖⠻"] # 0x32bb + - "㊼": [tc: "⠫⠉⠸⠫⠼⠲⠶⠻"] # 0x32bc + - "㊽": [tc: "⠫⠉⠸⠫⠼⠲⠦⠻"] # 0x32bd + - "㊾": [tc: "⠫⠉⠸⠫⠼⠲⠔⠻"] # 0x32be + - "㊿": [tc: "⠫⠉⠸⠫⠼⠢⠴⠻"] # 0x32bf + +#NOTE: uncertainty here + - "Ⓐ-Ⓩ": # 0x24b6 - 0x24cf + - tc: "⠫⠉⠸⠫⠠" + - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠻" + + - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 + - tc: "⠫⠉⠸⠫" + - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" + - tc: "⠻" + + # --- Nemeth Default Alphabet circled-sans-serif chars. --- + + - "➀-➈": # 0x1f10b, 0x2780 - 0x2788 + - tc: "⠫⠉⠸⠫⠠⠨⠼" + - spell: "translate('.', '➀➁➂➃➄➅➆➇➈', '123456789')" + - tc: "⠻" + + - "🄋": [tc: "⠫⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10b + - "➉": [tc: "⠫⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2789 + + # --- Nemeth Default Alphabet comma chars. --- + + - "🄁-🄊": # 0x1f101 - 0x1f10a + - spell: "translate('.', '🄁🄂🄃🄄🄅🄆🄇🄈🄉🄊', '0123456789')" + - tc: "⠠" + + + # --- Nemeth Default Alphabet double-circled chars. --- + + - "⓵": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠻⠻"] # 0x24f5 + - "⓶": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠆⠻⠻"] # 0x24f6 + - "⓷": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠒⠻⠻"] # 0x24f7 + - "⓸": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠲⠻⠻"] # 0x24f8 + - "⓹": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠢⠻⠻"] # 0x24f9 + - "⓺": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠖⠻⠻"] # 0x24fa + - "⓻": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠶⠻⠻"] # 0x24fb + - "⓼": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠦⠻⠻"] # 0x24fc + - "⓽": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠔⠻⠻"] # 0x24fd + - "⓾": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x24fe + + # --- Nemeth Default Alphabet double-struck chars. --- + - "𝔸-𝕐": # 0x1d538 - 0x1d550 (partial, excludes ℂ ℍ ℕ ℙ ℚ ℝ ℤ) + - tc: "𝔹E" + - spell: "translate('.', '𝔸𝔹𝔻𝔼𝔽𝔾𝕀𝕁𝕂𝕃𝕄𝕆𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABDEFGIJKLMOSTUVWXY')" + + - "ℂ": [tc: "𝔹ECL⠉"] # 0x2102 + - "ℍ": [tc: "𝔹ECL⠓"] # 0x210d + - "ℕ": [tc: "𝔹ECL⠝"] # 0x2115 + - "ℙ": [tc: "𝔹ECL⠏"] # 0x2119 + - "ℚ": [tc: "𝔹ECL⠟"] # 0x211a + - "ℝ": [tc: "𝔹ECL⠗"] # 0x211d + - "ℤ": [tc: "𝔹ECL⠵"] # 0x2124 + - "Ω": [t: "CGL⠺"] # 0x2126 (Ohm sign (capital Greek omega)) + + - "𝕒-𝕫": # 0x1d552 - 0x1d56b + - tc: "𝔹E" + - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 + - tc: "𝔹" + - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" + + + # --- Nemeth Default Alphabet fraktur chars. --- + + - "𝔄-𝔜": # 0x1d504 - 0x1d51c + - tc: "D" + - spell: "translate('.', '𝔄𝔅𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABDEFGJKLMNOPQSTUVWXY')" + + - "ℭ": [tc: "DCL⠉"] # 0x212d + - "ℌ": [tc: "DCL⠓"] # 0x210c + - "ℑ": [tc: "DCL⠊"] # 0x2111 + - "ℜ": [tc: "DCL⠗"] # 0x211c + - "ℨ": [tc: "DCL⠵"] # 0x2128 + + - "𝔞-𝔷": # 0x1d51e - 0x1d537 + - tc: "D" + - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet fullwidth chars. --- + + - "A-Z": # 0xff21 - 0xff3a + - tc: "" + - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "a-z": # 0xff41 - 0xff5a + - tc: "" + - spell: "translate('.', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz')" + + - "0-9": # 0xff10 - 0xff19 + - tc: "" + - spell: "translate('.', '0123456789', '0123456789')" + + # --- Nemeth Default Alphabet italic chars -- treat as non-italic. --- + + - "𝐴-𝑍": # 0x1d434 - 0x1d44d + - tc: "E" + - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝑎-𝑧": # 0x1d44e - 0x1d467 + - tc: "E" + - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔ℎ𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa + - tc: "G" + - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝛻": [tc: "GL⠫"] # 0x1d6fb + + - "𝛼-𝜌": # 0x1d6fc - 0x1d70c + - tc: "G" + - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌', 'αβγδεζηθικλμνξοπρ')" + - "𝜍": [tc: "VL⠎"] # 0x1d70d + + - "𝜎": [tc: "GL⠎"] # 0x1d70e + - "𝜏": [tc: "GL⠞"] # 0x1d70f + - "𝜐": [tc: "GL⠥"] # 0x1d710 + + - "𝜑": [tc: "VL⠋"] # 0x1d711 + + - "𝜒": [tc: "GL⠯"] # 0x1d712 + - "𝜓": [tc: "GL⠽"] # 0x1d713 + - "𝜔": [tc: "GL⠺"] # 0x1d714 + + - "𝜕": [tc: "VL⠙"] # 0x1d715 + + - "𝜖": [tc: "GL⠑"] # 0x1d716 + + - "𝜗": [tc: "VL⠹"] # 0x1d717 + + - "𝜘": [tc: "GL⠅"] # 0x1d718 + - "𝜙": [tc: "GL⠋"] # 0x1d719 + - "𝜚": [tc: "GL⠗"] # 0x1d71a + - "𝜛": [tc: "GL⠏"] # 0x1d71b + + # --- Nemeth Default Alphabet monospace chars. --- + + - "𝙰-𝚉": # 0x1d670 - 0x1d689 + - tc: "" + - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 + - tc: "" + - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff + - tc: "" + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + + + # --- Nemeth Default Alphabet negative-circled chars. --- + - "⓿": [t: "⠫⠸⠉⠸⠫⠼⠴⠻"] # 0x24ff + - "❶": [t: "⠫⠸⠉⠸⠫⠼⠂⠻"] # 0x2776 + - "❷": [t: "⠫⠸⠉⠸⠫⠼⠆⠻"] # 0x2777 + - "❸": [t: "⠫⠸⠉⠸⠫⠼⠒⠻"] # 0x2778 + - "❹": [t: "⠫⠸⠉⠸⠫⠼⠲⠻"] # 0x2779 + - "❺": [t: "⠫⠸⠉⠸⠫⠼⠢⠻"] # 0x277a + - "❻": [t: "⠫⠸⠉⠸⠫⠼⠖⠻"] # 0x277b + - "❼": [t: "⠫⠸⠉⠸⠫⠼⠶⠻"] # 0x277c + - "❽": [t: "⠫⠸⠉⠸⠫⠼⠦⠻"] # 0x277d + - "❾": [t: "⠫⠸⠉⠸⠫⠼⠔⠻"] # 0x277e + - "❿": [t: "⠫⠸⠉⠸⠫⠼⠂⠴⠻"] # 0x277f + - "⓫": [t: "⠫⠸⠉⠸⠫⠼⠂⠂⠻"] # 0x24eb + - "⓬": [t: "⠫⠸⠉⠸⠫⠼⠂⠆⠻"] # 0x24ec + - "⓭": [t: "⠫⠸⠉⠸⠫⠼⠂⠒⠻"] # 0x24ed + - "⓮": [t: "⠫⠸⠉⠸⠫⠼⠂⠲⠻"] # 0x24ee + - "⓯": [t: "⠫⠸⠉⠸⠫⠼⠂⠢⠻"] # 0x24ef + - "⓰": [t: "⠫⠸⠉⠸⠫⠼⠂⠖⠻"] # 0x24f0 + - "⓱": [t: "⠫⠸⠉⠸⠫⠼⠂⠶⠻"] # 0x24f1 + - "⓲": [t: "⠫⠸⠉⠸⠫⠼⠂⠦⠻"] # 0x24f2 + - "⓳": [t: "⠫⠸⠉⠸⠫⠼⠂⠔⠻"] # 0x24f3 + - "⓴": [t: "⠫⠸⠉⠸⠫⠼⠆⠴⠻"] # 0x24f4 + + - "🅐": [t: "⠫⠸⠉⠸⠫⠠⠁⠻"] # 0x1f150 + - "🅑": [t: "⠫⠸⠉⠸⠫⠠⠃⠻"] # 0x1f151 + - "🅒": [t: "⠫⠸⠉⠸⠫⠠⠉⠻"] # 0x1f152 + - "🅓": [t: "⠫⠸⠉⠸⠫⠠⠙⠻"] # 0x1f153 + - "🅔": [t: "⠫⠸⠉⠸⠫⠠⠑⠻"] # 0x1f154 + - "🅕": [t: "⠫⠸⠉⠸⠫⠠⠋⠻"] # 0x1f155 + - "🅖": [t: "⠫⠸⠉⠸⠫⠠⠛⠻"] # 0x1f156 + - "🅗": [t: "⠫⠸⠉⠸⠫⠠⠓⠻"] # 0x1f157 + - "🅘": [t: "⠫⠸⠉⠸⠫⠠⠊⠻"] # 0x1f158 + - "🅙": [t: "⠫⠸⠉⠸⠫⠠⠚⠻"] # 0x1f159 + - "🅚": [t: "⠫⠸⠉⠸⠫⠠⠅⠻"] # 0x1f15a + - "🅛": [t: "⠫⠸⠉⠸⠫⠠⠇⠻"] # 0x1f15b + - "🅜": [t: "⠫⠸⠉⠸⠫⠠⠍⠻"] # 0x1f15c + - "🅝": [t: "⠫⠸⠉⠸⠫⠠⠝⠻"] # 0x1f15d + - "🅞": [t: "⠫⠸⠉⠸⠫⠠⠕⠻"] # 0x1f15e + - "🅟": [t: "⠫⠸⠉⠸⠫⠠⠏⠻"] # 0x1f15f + - "🅠": [t: "⠫⠸⠉⠸⠫⠠⠟⠻"] # 0x1f160 + - "🅡": [t: "⠫⠸⠉⠸⠫⠠⠗⠻"] # 0x1f161 + - "🅢": [t: "⠫⠸⠉⠸⠫⠠⠎⠻"] # 0x1f162 + - "🅣": [t: "⠫⠸⠉⠸⠫⠠⠞⠻"] # 0x1f163 + - "🅤": [t: "⠫⠸⠉⠸⠫⠠⠥⠻"] # 0x1f164 + - "🅥": [t: "⠫⠸⠉⠸⠫⠠⠧⠻"] # 0x1f165 + - "🅦": [t: "⠫⠸⠉⠸⠫⠠⠺⠻"] # 0x1f166 + - "🅧": [t: "⠫⠸⠉⠸⠫⠠⠭⠻"] # 0x1f167 + - "🅨": [t: "⠫⠸⠉⠸⠫⠠⠽⠻"] # 0x1f168 + - "🅩": [t: "⠫⠸⠉⠸⠫⠠⠵⠻"] # 0x1f169 + + # --- Nemeth Default Alphabet negative-circled-sans-serif chars. --- + - "🄌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10c + - "➊": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠻"] # 0x278a + - "➋": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠆⠻"] # 0x278b + - "➌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠒⠻"] # 0x278c + - "➍": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠲⠻"] # 0x278d + - "➎": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠢⠻"] # 0x278e + - "➏": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠖⠻"] # 0x278f + - "➐": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠶⠻"] # 0x2790 + - "➑": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠦⠻"] # 0x2791 + - "➒": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠔⠻"] # 0x2792 + - "➓": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2793 + + # --- Nemeth Default Alphabet negative-squared chars. --- + - "🅰-🆉": # 0x1f170 - 0x1f189 + - tc: "⠫⠸⠲⠸⠫⠠" + - spell: "translate('.', '🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠻" + + # --- Nemeth Default Alphabet parenthesized chars. --- + + - "⑴-⑼": # 0x2474 - 0x247c + - tc: "⠷" + - spell: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" + - tc: "⠾" + + - "⑽": [t: "⠷⠂⠴⠾"] # 0x247d + - "⑾": [t: "⠷⠂⠂⠾"] # 0x247e + - "⑿": [t: "⠷⠂⠆⠾"] # 0x247f + - "⒀": [t: "⠷⠂⠒⠾"] # 0x2480 + - "⒁": [t: "⠷⠂⠲⠾"] # 0x2481 + - "⒂": [t: "⠷⠂⠢⠾"] # 0x2482 + - "⒃": [t: "⠷⠂⠖⠾"] # 0x2483 + - "⒄": [t: "⠷⠂⠶⠾"] # 0x2484 + - "⒅": [t: "⠷⠂⠦⠾"] # 0x2485 + - "⒆": [t: "⠷⠂⠔⠾"] # 0x2486 + - "⒇": [t: "⠷⠆⠴⠾"] # 0x2487 + + - "🄐-🄩": # 0x1f110 - 0x1f129 + - tc: "⠷⠠" + - spell: "translate('.', '🄐🄑🄒🄓🄔🄕🄖🄗🄘🄙🄚🄛🄜🄝🄞🄟🄠🄡🄢🄣🄤🄥🄦🄧🄨🄩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠾" + + - "⒜-⒵": # 0x249c - 0x24b5 + - tc: "⠷" + - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" + - tc: "⠾" + + # --- Nemeth Default Alphabet period chars. --- + - "🄀": [t: "⠴⠸⠲"] # 0x1f100 + - "⒈": [t: "⠂⠸⠲"] # 0x2488 + - "⒉": [t: "⠆⠸⠲"] # 0x2489 + - "⒊": [t: "⠒⠸⠲"] # 0x248a + - "⒋": [t: "⠲⠸⠲"] # 0x248b + - "⒌": [t: "⠢⠸⠲"] # 0x248c + - "⒍": [t: "⠖⠸⠲"] # 0x248d + - "⒎": [t: "⠶⠸⠲"] # 0x248e + - "⒏": [t: "⠦⠸⠲"] # 0x248f + - "⒐": [t: "⠔⠸⠲"] # 0x2490 + + - "⒑": [t: "⠂⠴⠸⠲"] # 0x2491 + - "⒒": [t: "⠂⠂⠸⠲"] # 0x2492 + - "⒓": [t: "⠂⠆⠸⠲"] # 0x2493 + - "⒔": [t: "⠂⠒⠸⠲"] # 0x2494 + - "⒕": [t: "⠂⠲⠸⠲"] # 0x2495 + - "⒖": [t: "⠂⠢⠸⠲"] # 0x2496 + - "⒗": [t: "⠂⠖⠸⠲"] # 0x2497 + - "⒘": [t: "⠂⠶⠸⠲"] # 0x2498 + - "⒙": [t: "⠂⠦⠸⠲"] # 0x2499 + - "⒚": [t: "⠂⠔⠸⠲"] # 0x249a + - "⒛": [t: "⠆⠴⠸⠲"] # 0x249b + + # --- Nemeth Default Alphabet sans-serif chars. --- + - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 + - tc: "SE" + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 + - tc: "SE" + - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb + - tc: "S" + - spell: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" + + # --- Nemeth Default Alphabet sans-serif-bold chars. --- + - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed + - tc: "SBE" + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝗮-𝘇": # 0x1d5ee - 0x1d607 + - tc: "SBE" + - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝝖-𝝮": # 0x1d756 - 0x1d76e + - tc: "SBG" + - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝝰-𝞀": # 0x1d770 - 0x1d780 + - tc: "SBG" + - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀', 'αβγδεζηθικλμνξοπρ')" + + - "𝝯": [tc: "SBGL⠫"] # 0x1d76f + + - "𝞁": [tc: "SBVL⠎"] # 0x1d781 + + - "𝞂-𝞄": # 0x1d782 - 0x1d784 + - tc: "SBG" + - spell: "translate('.', '𝞂𝞃𝞄', 'στυ')" + + - "𝞅": [tc: "SBVL⠋"] # 0x1d785 + + - "𝞆-𝞈": # 0x1d786 - 0x1d788 + - tc: "SBG" + - spell: "translate('.', '𝞆𝞇𝞈', 'χψω')" + + - "𝞉": [tc: "⠠⠨⠸⠈⠙"] # 0x1d789 + - "𝞊": [tc: "SBGL⠑"] # 0x1d78a + - "𝞋": [tc: "SBVL⠹"] # 0x1d78b + + - "𝞌-𝞏": # 0x1d78c - 0x1d78f + - tc: "SBG" + - spell: "translate('.', '𝞌𝞍𝞎𝞏', 'κφρπ')" + + - "𝟬-𝟵": # 0x1d7ec - 0x1d7f5 + - tc: "SB" + - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" + + # --- Nemeth Default Alphabet sans-serif-bold-italic chars. --- + + - "𝘼-𝙕": # 0x1d63c - 0x1d655 + - tc: "SBE" + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝙖-𝙯": # 0x1d656 - 0x1d66f + - tc: "SBE" + - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 + - tc: "SBG" + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝞩": [t: "SBGL⠫"] # 0x1d7a9 + + - "𝞪-𝞺": # 0x1d7aa - 0x1d7ba + - tc: "SBG" + - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺', 'αβγδεζηθικλμνξοπρ')" + + - "𝞻": [t: "SBVL⠎"] # 0x1d7bb + - "𝞼": [t: "SBGL⠎"] # 0x1d7bc + - "𝞽": [t: "SBGL⠞"] # 0x1d7bd + - "𝞾": [t: "SBGL⠥"] # 0x1d7be + - "𝞿": [t: "SBVL⠋"] # 0x1d7bf + + - "𝟀": [t: "SBGL⠯"] # 0x1d7c0 + - "𝟁": [t: "SBGL⠽"] # 0x1d7c1 + - "𝟂": [t: "SBGL⠺"] # 0x1d7c2 + - "𝟃": [t: "SBVL⠙"] # 0x1d7c3 + + - "𝟄": [t: "SBGL⠑"] # 0x1d7c4 + - "𝟅": [t: "SBVL⠹"] # 0x1d7c5 + + - "𝟆": [t: "SBGL⠅"] # 0x1d7c6 + - "𝟇": [t: "SBGL⠋"] # 0x1d7c7 + - "𝟈": [t: "SBGL⠗"] # 0x1d7c8 + - "𝟉": [t: "SBGL⠏"] # 0x1d7c9 + + # --- Nemeth Default Alphabet sans-serif-italic chars. --- + - "𝘈-𝘡": # 0x1d608 - 0x1d621 + - tc: "SE" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝘢-𝘻": # 0x1d622 - 0x1d63b + - tc: "SE" + - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet script chars. --- + - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 + - tc: "TE" + - spell: "translate('.', '𝒜𝒞𝒟𝒢𝒥𝒦𝒩𝒪𝒫𝒬𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ACDGJKNOPQSTUVWXYZ')" + + - "ℬ": [t: "TECL⠃"] # 0x212c + - "ℰ": [t: "TECL⠑"] # 0x2130 + - "ℱ": [t: "TECL⠋"] # 0x2131 + - "ℋ": [t: "TECL⠓"] # 0x210b + - "ℐ": [t: "TECL⠊"] # 0x2110 + - "ℒ": [t: "TECL⠇"] # 0x2112 + - "ℳ": [t: "TECL⠍"] # 0x2133 + - "ℛ": [t: "TECL⠗"] # 0x211b + + - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf + - tc: "TE" + - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒻𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdfhijklmnpqrstuvwxyz')" + + - "ℯ": [t: "TEL⠑"] # 0x212f + - "ℊ": [t: "TEL⠛"] # 0x210a + - "ℴ": [t: "TEL⠕"] # 0x2134 + + # --- Nemeth Default Alphabet squared chars. --- + + - "🄰": [t: "⠫⠲⠸⠫⠠⠁⠻"] # 0x1f130 + - "🄱": [t: "⠫⠲⠸⠫⠠⠃⠻"] # 0x1f131 + - "🄲": [t: "⠫⠲⠸⠫⠠⠉⠻"] # 0x1f132 + - "🄳": [t: "⠫⠲⠸⠫⠠⠙⠻"] # 0x1f133 + - "🄴": [t: "⠫⠲⠸⠫⠠⠑⠻"] # 0x1f134 + - "🄵": [t: "⠫⠲⠸⠫⠠⠋⠻"] # 0x1f135 + - "🄶": [t: "⠫⠲⠸⠫⠠⠛⠻"] # 0x1f136 + - "🄷": [t: "⠫⠲⠸⠫⠠⠓⠻"] # 0x1f137 + - "🄸": [t: "⠫⠲⠸⠫⠠⠊⠻"] # 0x1f138 + - "🄹": [t: "⠫⠲⠸⠫⠠⠚⠻"] # 0x1f139 + - "🄺": [t: "⠫⠲⠸⠫⠠⠅⠻"] # 0x1f13a + - "🄻": [t: "⠫⠲⠸⠫⠠⠇⠻"] # 0x1f13b + - "🄼": [t: "⠫⠲⠸⠫⠠⠍⠻"] # 0x1f13c + - "🄽": [t: "⠫⠲⠸⠫⠠⠝⠻"] # 0x1f13d + - "🄾": [t: "⠫⠲⠸⠫⠠⠕⠻"] # 0x1f13e + - "🄿": [t: "⠫⠲⠸⠫⠠⠏⠻"] # 0x1f13f + - "🅀": [t: "⠫⠲⠸⠫⠠⠟⠻"] # 0x1f140 + - "🅁": [t: "⠫⠲⠸⠫⠠⠗⠻"] # 0x1f141 + - "🅂": [t: "⠫⠲⠸⠫⠠⠎⠻"] # 0x1f142 + - "🅃": [t: "⠫⠲⠸⠫⠠⠞⠻"] # 0x1f143 + - "🅄": [t: "⠫⠲⠸⠫⠠⠥⠻"] # 0x1f144 + - "🅅": [t: "⠫⠲⠸⠫⠠⠧⠻"] # 0x1f145 + - "🅆": [t: "⠫⠲⠸⠫⠠⠺⠻"] # 0x1f146 + - "🅇": [t: "⠫⠲⠸⠫⠠⠭⠻"] # 0x1f147 + - "🅈": [t: "⠫⠲⠸⠫⠠⠽⠻"] # 0x1f148 + - "🅉": [t: "⠫⠲⠸⠫⠠⠵⠻"] # 0x1f149 + + # --- Nemeth Default Alphabet sub chars. --- + + - "₀": [t: "⠰⠴"] # 0x2080 + - "₁": [t: "⠰⠂"] # 0x2081 + - "₂": [t: "⠰⠆"] # 0x2082 + - "₃": [t: "⠰⠒"] # 0x2083 + - "₄": [t: "⠰⠲"] # 0x2084 + - "₅": [t: "⠰⠢"] # 0x2085 + - "₆": [t: "⠰⠖"] # 0x2086 + - "₇": [t: "⠰⠶"] # 0x2087 + - "₈": [t: "⠰⠦"] # 0x2088 + - "₉": [t: "⠰⠔"] # 0x2089 + + # --- Nemeth Default Alphabet super chars. --- + + - "⁰": [t: "⠘⠴"] # 0x2070 + - "¹": [t: "⠘⠂"] # 0xb9 + - "²": [t: "⠘⠆"] # 0xb2 + - "³": [t: "⠘⠒"] # 0xb3 + - "⁴": [t: "⠘⠲"] # 0x2074 + - "⁵": [t: "⠘⠢"] # 0x2075 + - "⁶": [t: "⠘⠖"] # 0x2076 + - "⁷": [t: "⠘⠶"] # 0x2077 + - "⁸": [t: "⠘⠦"] # 0x2078 + - "⁹": [t: "⠘⠔"] # 0x2079 + + # --- Nemeth Default Characters latin-lower-phonetic chars. --- + + - "ø": [t: "⠈⠕"] # 0xf8 + + # --- Nemeth Default Characters math_geometry chars. --- + + - "─": [t: "⠱"] # 0x2500 + - "│": [t: "⠳"] # 0x2502 + - "▰": [t: "⠫⠸⠛"] # 0x25b0 + - "▱": [t: "⠫⠛"] # 0x25b1 + - "▽": [t: "⠨⠫"] # 0x25bd + - "◆": [t: "⠫⠸⠙"] # 0x25c6 + - "◇": [t: "⠫⠙"] # 0x25c7 + - "◈": [t: "⠫⠸⠙"] # 0x25c8 + - "◉": [t: "⠫⠸⠉"] # 0x25c9 + - "◊": [t: "⠫⠙"] # 0x25ca + - "⬟": [t: "⠫⠸⠢"] # 0x2b1f + - "⬠": [t: "⠫⠢"] # 0x2b20 + - "⬡": [t: "⠫⠖"] # 0x2b21 + - "⬢": [t: "⠫⠸⠖"] # 0x2b22 + - "⬣": [t: "⠫⠸⠖"] # 0x2b23 + - "⬤": [t: "⠫⠸⠉"] # 0x2b24 + - "⬥": [t: "⠫⠸⠙"] # 0x2b25 + + # --- Nemeth Default Characters math_symbols chars. --- + + - "\"": [t: "⠄⠄"] # 0x22 + - "¡": [t: "⠖"] # 0xa1 + - "¢": [t: "⠈⠉"] # 0xa2 + - "£": [t: "⠈⠇"] # 0xa3 + - "¤": [t: "⠈⠫"] # 0xa4 + - "¥": [t: "⠈⠽"] # 0xa5 + - "§": [t: "⠈⠠⠎"] # 0xa7 + - "©": [t: "⠘⠉"] # 0xa9 (Copyright) + - "ª": [t: "⠸⠰⠁"] # 0xaa + - "«": [t: "⠐⠅⠈⠐⠅⠻"] # 0xab + - "®": [t: "⠘⠗"] # 0xae + - "´": [t: "⠈"] # 0xb4 + - "µ": [t: "⠨⠍"] # 0xb5 + - "¶": [t: "⠈⠠⠏"] # 0xb6 + - "¿": [t: "⠦"] # 0xbf + - "ʹ": [t: "⠄"] # 0x2b9 + - "ʺ": [t: "⠄⠄"] # 0x2ba + - "˙": [t: "⠡"] # 0x2d9 + - "˜": [t: "⠈⠱"] # 0x2dc + - "‐": [t: "⠤"] # 0x2010 + - "‑": [t: "⠤"] # 0x2011 + - "‒": [t: "⠤⠤"] # 0x2012 + - "–": [t: "⠤⠤"] # 0x2013 + - "‖": [t: "⠳⠳"] # 0x2016 + - "†": [t: "⠸⠻"] # 0x2020 + - "‡": [t: "⠸⠸⠻"] # 0x2021 + - "•": [t: "⠡"] # 0x2022 + - "․": [t: "⠄"] # 0x2024 + - "‥": [t: "⠄⠄"] # 0x2025 + - "′": [t: "⠄"] # 0x2032 + - "″": [t: "⠄⠄"] # 0x2033 + - "‴": [t: "⠄⠄."] # 0x2034 (Triple prime) + - "⁗": [t: "⠄⠄.."] # 0x2057 (Quadruple prime) + - "‼": [t: "⠖⠖"] # 0x203c + - "‾": [t: "⠱"] # 0x203e + - "⁇": [t: "⠹⠹"] # 0x2047 + - "⁈": [t: "⠹⠖"] # 0x2048 + - "⁉": [t: "⠖⠹"] # 0x2049 + - "∀": [t: "⠈⠯"] # 0x2200 + - "∃": [t: "⠈⠿"] # 0x2203 + - "∄": [t: "⠌⠈⠿"] # 0x2204 + - "∅": [t: "⠸⠴"] # 0x2205 + - "∈": [t: "⠈⠑"] # 0x2208 + - "∉": [t: "⠌⠈⠑"] # 0x2209 + - "∊": [t: "⠈⠑"] # 0x220a + - "∋": [t: "⠈⠢"] # 0x220b + - "∌": [t: "⠌⠈⠢"] # 0x220c + - "∍": [t: "⠈⠢"] # 0x220d + - "∎": [t: "⠸⠳"] # 0x220e + - "−": [t: "⠤"] # 0x2212 + - "∓": [t: "⠤⠬"] # 0x2213 + - "∕": [t: "⠸⠌"] # 0x2215 + - "∖": [t: "⠸⠡"] # 0x2216 + - "∗": [t: "⠈⠼"] # 0x2217 + - "∘": [t: "⠨⠡"] # 0x2218 + - "∝": [t: "⠸⠿"] # 0x221d + - "∞": [t: "⠠⠿"] # 0x221e + - "∟": [t: "⠫⠪⠨⠗⠻"] # 0x221f + - "∠": [t: "⠫⠪"] # 0x2220 + - "∢": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2222 + - "∣": [t: "⠳"] # 0x2223 + - "∤": [t: "⠌⠳"] # 0x2224 + - "∥": [t: "⠫⠇"] # 0x2225 + - "∦": [t: "⠌⠫⠇"] # 0x2226 + - "∧": [t: "⠈⠩"] # 0x2227 + - "∨": [t: "⠈⠬"] # 0x2228 + - "∩": [t: "⠨⠩"] # 0x2229 + - "∪": [t: "⠨⠬"] # 0x222a + - "∫": [t: "⠮"] # 0x222b + - "∬": [t: "⠮⠮"] # 0x222c + - "∭": [t: "⠮⠮⠮"] # 0x222d + - "∮": [t: "⠮⠈⠫⠉⠻"] # 0x222e + - "∲": [t: "⠮⠈⠫⠪⠢⠔⠻"] # 0x2232 + - "∳": [t: "⠮⠈⠫⠢⠔⠕⠻"] # 0x2233 + - "∴": [t: "⠠⠡"] # 0x2234 + - "∵": [t: "⠈⠌"] # 0x2235 + - "∶": [t: "⠐⠂"] # 0x2236 + - "∷": [t: "⠰⠆"] # 0x2237 + - "∸": [t: "⠨⠤"] # 0x2238 + - "∼": [t: "⠈⠱"] # 0x223c + - "≁": [t: "⠌⠈⠱"] # 0x2241 + - "≂": [t: "⠱⠈⠱"] # 0x2242 + - "≃": [t: "⠈⠱⠱"] # 0x2243 + - "≄": [t: "⠌⠈⠱⠱"] # 0x2244 + - "≅": [t: "⠈⠱⠨⠅"] # 0x2245 + - "≆": [t: "⠈⠱⠌⠨⠅"] # 0x2246 + - "≇": [t: "⠌⠈⠱⠨⠅"] # 0x2247 + - "≈": [t: "⠈⠱⠈⠱"] # 0x2248 + - "≉": [t: "⠌⠈⠱⠈⠱"] # 0x2249 + - "≊": [t: "⠈⠱⠈⠱⠱"] # 0x224a + - "≍": [t: "⠈⠣⠠⠣"] # 0x224d + - "≎": [t: "⠈⠣⠠⠣"] # 0x224e + - "≐": [t: "⠐⠨⠅⠣⠡⠻"] # 0x2250 + - "≑": [t: "⠐⠨⠅⠩⠡⠣⠡⠻"] # 0x2251 + - "≗": [t: "⠐⠨⠅⠣⠨⠡⠻"] # 0x2257 + - "≘": [t: "⠐⠨⠅⠣⠫⠁⠻"] # 0x2258 + - "≙": [t: "⠐⠨⠅⠣⠸⠣⠻"] # 0x2259 + - "≚": [t: "⠐⠨⠅⠣⠸⠩⠻"] # 0x225a + - "≛": [t: "⠐⠨⠅⠣⠫⠸⠎⠻"] # 0x225b + - "≜": [t: "⠐⠨⠅⠣⠫⠞⠻"] # 0x225c + - "≝": [t: "⠐⠨⠅⠣⠙⠑⠋⠻"] # 0x225d + - "≞": [t: "⠐⠨⠅⠣⠍⠻"] # 0x225e + - "≟": [t: "⠐⠨⠅⠣⠸⠦⠻"] # 0x225f + - "≠": [t: "⠌⠨⠅"] # 0x2260 + - "≢": [t: "⠌⠸⠇"] # 0x2262 + - "≤": [t: "⠐⠅⠱"] # 0x2264 + - "≥": [t: "⠨⠂⠱"] # 0x2265 + - "≦": [t: "⠐⠅⠨⠅"] # 0x2266 + - "≧": [t: "⠨⠂⠨⠅"] # 0x2267 + - "≨": [t: "⠐⠅⠌⠨⠅"] # 0x2268 + - "≩": [t: "⠨⠂⠌⠨⠅"] # 0x2269 + - "≪": [t: "⠐⠅⠈⠐⠅⠻"] # 0x226a + - "≫": [t: "⠨⠂⠈⠨⠂⠻"] # 0x226b + - "≭": [t: "⠌⠈⠣⠠⠣"] # 0x226d + - "≮": [t: "⠌⠐⠅"] # 0x226e + - "≯": [t: "⠌⠨⠂"] # 0x226f + - "≰": [t: "⠌⠐⠅⠱"] # 0x2270 + - "≱": [t: "⠌⠨⠂⠱"] # 0x2271 + - "≲": [t: "⠐⠅⠈⠱"] # 0x2272 + - "≳": [t: "⠨⠂⠈⠱"] # 0x2273 + - "≴": [t: "⠌⠐⠅⠈⠱"] # 0x2274 + - "≵": [t: "⠌⠨⠂⠈⠱"] # 0x2275 + - "≶": [t: "⠐⠅⠨⠂"] # 0x2276 + - "≷": [t: "⠨⠂⠐⠅"] # 0x2277 + - "≸": [t: "⠌⠐⠅⠨⠂"] # 0x2278 + - "≹": [t: "⠌⠨⠂⠐⠅"] # 0x2279 + - "≺": [t: "⠨⠐⠅"] # 0x227a + - "≻": [t: "⠨⠨⠂"] # 0x227b + - "≼": [t: "⠨⠐⠅⠱"] # 0x227c + - "≽": [t: "⠨⠨⠐⠱"] # 0x227d + - "≾": [t: "⠨⠐⠅⠈⠱"] # 0x227e + - "≿": [t: "⠨⠨⠂⠈⠱"] # 0x227f + - "⊀": [t: "⠌⠨⠐⠅"] # 0x2280 + - "⊁": [t: "⠌⠨⠐⠅"] # 0x2281 + - "⊂": [t: "⠸⠐⠅"] # 0x2282 + - "⊃": [t: "⠸⠨⠂"] # 0x2283 + - "⊄": [t: "⠌⠸⠐⠅"] # 0x2284 + - "⊅": [t: "⠌⠸⠨⠂"] # 0x2285 + - "⊆": [t: "⠸⠐⠅⠱"] # 0x2286 + - "⊇": [t: "⠸⠨⠂⠱"] # 0x2287 + - "⊈": [t: "⠌⠸⠐⠅⠱"] # 0x2288 + - "⊉": [t: "⠌⠸⠨⠂⠱"] # 0x2289 + - "⊊": [t: "⠸⠐⠅⠌⠨⠅"] # 0x228a + - "⊋": [t: "⠸⠨⠂⠌⠨⠅"] # 0x228b + - "⊥": [t: "⠫⠏"] # 0x22a5 + - "⋀": [t: "⠈⠩"] # 0x22c0 + - "⋁": [t: "⠈⠬"] # 0x22c1 + - "⋂": [t: "⠨⠩"] # 0x22c2 + - "⋃": [t: "⠨⠬"] # 0x22c3 + - "⋄": [t: "⠫⠙"] # 0x22c4 + - "⋜": [t: "⠱⠐⠅"] # 0x22dc + - "⋝": [t: "⠱⠨⠂"] # 0x22dd + - "⟂": [t: "⠫⠏"] # 0x27c2 + - "⦀": [t: "⠳⠳⠳"] # 0x2980 + - "⦶": [t: "⠫⠉⠸⠫⠳⠻"] # 0x29b6 + - "⦷": [t: "⠫⠉⠸⠫⠫⠇⠻"] # 0x29b7 + - "⦸": [t: "⠫⠉⠸⠫⠸⠡⠻"] # 0x29b8 + - "⦹": [t: "⠫⠉⠸⠫⠫⠏⠻"] # 0x29b9 + - "⦿": [t: "⠫⠉⠸⠫⠔⠔⠻"] # 0x29bf + - "⧀": [t: "⠫⠉⠸⠫⠐⠅⠻"] # 0x29c0 + - "⧁": [t: "⠫⠉⠸⠫⠨⠂⠻"] # 0x29c1 + - "⨀": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2a00 + - "⨁": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2a01 + - "⨂": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2a02 + - "⪯": [t: "⠨⠐⠅⠱"] # 0x2aaf + - "⪰": [t: "⠨⠨⠂⠱"] # 0x2ab0 + - "⪱": [t: "⠨⠐⠅⠌⠱"] # 0x2ab1 + - "⪲": [t: "⠨⠨⠂⠌⠱"] # 0x2ab2 + - "⪳": [t: "⠨⠐⠅⠨⠅"] # 0x2ab3 + - "⪴": [t: "⠨⠨⠂⠨⠅"] # 0x2ab4 + - "⪵": [t: "⠨⠐⠅⠌⠨⠅"] # 0x2ab5 + - "⪶": [t: "⠨⠨⠂⠌⠨⠅"] # 0x2ab6 + - "⪷": [t: "⠨⠐⠅⠈⠱⠈⠱"] # 0x2ab7 + - "⪸": [t: "⠨⠨⠂⠈⠱⠈⠱"] # 0x2ab8 + - "⪹": [t: "⠨⠐⠅⠌⠈⠱⠈⠱"] # 0x2ab9 + - "⪺": [t: "⠨⠨⠂⠌⠈⠱⠈⠱"] # 0x2aba + - "⪻": [t: "⠨⠐⠅⠈⠨⠐⠅⠻"] # 0x2abb + - "⪼": [t: "⠨⠨⠂⠈⠨⠨⠂⠻"] # 0x2abc + - "﹩": [t: "⠈⠎"] # 0xfe69 + - "$": [t: "⠈⠎"] # 0xff04 + - "_": [t: "⠱"] # 0xff3f + - "~": [t: "⠈⠱"] # 0xff5e + - "¢": [t: "⠈⠉"] # 0xffe0 + - "£": [t: "⠈⠇"] # 0xffe1 + - " ̄": [t: "⠱"] # 0xffe3 + - "¥": [t: "⠈⠽"] # 0xffe5 + + # --- Nemeth Default Characters rest chars. --- + + - "ℵ": [t: "⠠⠠⠁"] # 0x2135 + - "ℶ": [t: "⠠⠠⠃"] # 0x2136 + - "ℷ": [t: "⠠⠠⠉"] # 0x2137 + - "ℸ": [t: "⠠⠠⠙"] # 0x2138 + - "à": [t: "⠈⠁"] # 0xe0 + - "á": [t: "⠈⠁"] # 0xe1 + - "â": [t: "⠈⠁"] # 0xe2 + - "ã": [t: "⠈⠁"] # 0xe3 + - "ä": [t: "⠈⠁"] # 0xe4 + - "å": [t: "⠈⠁"] # 0xe5 + - "ç": [t: "⠈⠉"] # 0xe7 + - "è": [t: "⠈⠑"] # 0xe8 + - "é": [t: "⠈⠑"] # 0xe9 + - "ê": [t: "⠈⠑"] # 0xea + - "ë": [t: "⠈⠑"] # 0xeb + - "ì": [t: "⠈⠊"] # 0xec + - "í": [t: "⠈⠊"] # 0xed + - "î": [t: "⠈⠊"] # 0xee + - "ï": [t: "⠈⠊"] # 0xef + - "ñ": [t: "⠈⠝"] # 0xf1 + - "ò": [t: "⠈⠕"] # 0xf2 + - "ó": [t: "⠈⠕"] # 0xf3 + - "ô": [t: "⠈⠕"] # 0xf4 + - "õ": [t: "⠈⠕"] # 0xf5 + - "ö": [t: "⠈⠕"] # 0xf6 + - "ù": [t: "⠈⠥"] # 0xf9 + - "ú": [t: "⠈⠥"] # 0xfa + - "û": [t: "⠈⠥"] # 0xfb + - "ü": [t: "⠈⠥"] # 0xfc + - "ý": [t: "⠈⠽"] # 0xfd + - "ÿ": [t: "⠈⠽"] # 0xff + + - "À": [t: "⠠⠈⠁"] # 0xc0 + - "Á": [t: "⠠⠈⠁"] # 0xc1 + - "Â": [t: "⠠⠈⠁"] # 0xc2 + - "Ã": [t: "⠠⠈⠁"] # 0xc3 + - "Ä": [t: "⠠⠈⠁"] # 0xc4 + - "Å": [t: "⠠⠈⠁"] # 0xc5 + - "Ç": [t: "⠠⠈⠉"] # 0xc7 + - "È": [t: "⠠⠈⠑"] # 0xc8 + - "É": [t: "⠠⠈⠑"] # 0xc9 + - "Ê": [t: "⠠⠈⠑"] # 0xca + - "Ë": [t: "⠠⠈⠑"] # 0xcb + - "Ì": [t: "⠠⠈⠊"] # 0xcc + - "Í": [t: "⠠⠈⠊"] # 0xcd + - "Î": [t: "⠠⠈⠊"] # 0xce + - "Ï": [t: "⠠⠈⠊"] # 0xcf + - "Ñ": [t: "⠠⠈⠝"] # 0xd1 + - "Ò": [t: "⠠⠈⠕"] # 0xd2 + - "Ó": [t: "⠠⠈⠕"] # 0xd3 + - "Ô": [t: "⠠⠈⠕"] # 0xd4 + - "Ö": [t: "⠠⠈⠕"] # 0xd6 + - "Ù": [t: "⠠⠈⠥"] # 0xd9 + - "Ú": [t: "⠠⠈⠥"] # 0xda + - "Û": [t: "⠠⠈⠥"] # 0xdb + - "Ü": [t: "⠠⠈⠥"] # 0xdc + - "Ý": [t: "⠠⠈⠽"] # 0xdd + - "←": [t: "⠫⠪⠒⠒"] # 0x2190 + + - "↑": # 0x2191 + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠣"] + else: [t: "⠫⠣⠒⠒⠕"] + + - "↓": # 0x2193 + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠩"] + else: [t: "⠫⠩⠒⠒⠕"] + + - "↔": [t: "⠫⠪⠒⠒⠕"] # 0x2194 + - "↕": [t: "⠫⠣⠩⠪⠒⠒⠕"] # 0x2195 + - "↖": [t: "⠫⠘⠪⠒⠒"] # 0x2196 + - "↗": [t: "⠫⠘⠒⠒⠕"] # 0x2197 + - "↘": [t: "⠫⠰⠒⠒⠕"] # 0x2198 + - "↙": [t: "⠫⠰⠪⠒⠒"] # 0x2199 + - "↚": [t: "⠳⠈⠫⠪⠒⠒⠻"] # 0x219a + - "↛": [t: "⠳⠈⠫⠒⠒⠕⠻"] # 0x219b + - "↜": [t: "⠫⠪⠔⠒⠢"] # 0x219c + - "↝": [t: "⠫⠔⠒⠢⠕"] # 0x219d + - "↞": [t: "⠫⠪⠪⠒⠒"] # 0x219e + - "↟": [t: "⠫⠣⠒⠒⠕⠕"] # 0x219f + - "↠": [t: "⠫⠒⠒⠕⠕"] # 0x21a0 + - "↡": [t: "⠫⠩⠒⠒⠕⠕"] # 0x21a1 + - "↢": [t: "⠫⠪⠒⠒⠠⠽"] # 0x21a2 + - "↣": [t: "⠫⠠⠯⠒⠒⠕"] # 0x21a3 + - "↤": [t: "⠫⠪⠒⠒⠳"] # 0x21a4 + - "↥": [t: "⠫⠣⠳⠒⠒⠕"] # 0x21a5 + - "↦": [t: "⠫⠳⠒⠒⠕"] # 0x21a6 + - "↧": [t: "⠫⠩⠳⠒⠒⠕"] # 0x21a7 + - "↨": [t: "⠫⠪⠒⠳⠒⠕"] # 0x21a8 + - "↩": [t: "⠫⠪⠒⠒⠠⠕"] # 0x21a9 + - "↪": [t: "⠫⠠⠪⠒⠒⠕"] # 0x21aa + - "↫": [t: "⠫⠪⠒⠒⠨⠡"] # 0x21ab + - "↬": [t: "⠫⠨⠡⠒⠒⠕"] # 0x21ac + - "↭": [t: "⠫⠪⠔⠒⠢⠕"] # 0x21ad + - "↮": [t: "⠳⠈⠫⠪⠒⠒⠕"] # 0x21ae + - "↯": [t: "⠫⠩⠔⠢⠔"] # 0x21af + - "↴": [t: "⠫⠠⠳⠒⠒⠕"] # 0x21b4 + - "↵": [t: "⠫⠩⠠⠳⠒⠒⠕"] # 0x21b5 + - "↶": [t: "⠫⠢⠔⠕"] # 0x21b6 + - "↷": [t: "⠫⠪⠢⠔"] # 0x21b7 + - "↺": [t: "⠫⠢⠔⠕"] # 0x21ba + - "↻": [t: "⠫⠪⠢⠔"] # 0x21bb + - "⇄": [t: "⠫⠒⠒⠕⠫⠪⠒⠒"] # 0x21c4 + - "⇅": [t: "⠫⠣⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21c5 + - "⇆": [t: "⠫⠪⠒⠒⠫⠒⠒⠕"] # 0x21c6 + - "⇇": [t: "⠫⠚⠒⠒⠫⠚⠒⠒"] # 0x21c7 + - "⇈": [t: "⠫⠣⠒⠒⠕⠐⠫⠣⠒⠒⠕"] # 0x21c8 + - "⇉": [t: "⠫⠒⠒⠕⠫⠒⠒⠕"] # 0x21c9 + - "⇊": [t: "⠫⠩⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21ca + - "⇍": [t: "⠳⠈⠫⠪⠪⠒⠒"] # 0x21cd + - "⇎": [t: "⠳⠈⠫⠪⠪⠒⠒⠕⠕"] # 0x21ce + - "⇏": [t: "⠳⠈⠫⠒⠒⠕⠕"] # 0x21cf + - "⇐": [t: "⠫⠪⠶⠶"] # 0x21d0 + - "⇑": [t: "⠫⠣⠶⠶⠕"] # 0x21d1 + - "⇒": [t: "⠫⠶⠶⠕"] # 0x21d2 + - "⇓": [t: "⠫⠣⠶⠶⠕"] # 0x21d3 + - "⇔": [t: "⠫⠪⠶⠶⠕"] # 0x21d4 + - "⇕": [t: "⠫⠣⠪⠶⠶⠕"] # 0x21d5 + - "⇖": [t: "⠫⠘⠪⠶⠶"] # 0x21d6 + - "⇗": [t: "⠫⠘⠶⠶⠕"] # 0x21d7 + - "⇘": [t: "⠫⠰⠶⠶⠕"] # 0x21d8 + - "⇙": [t: "⠫⠰⠪⠪⠒⠒"] # 0x21d9 + - "⇚": [t: "⠫⠪⠪⠶⠶"] # 0x21da FIX: how to represent triple shafts? + - "⇛": [t: "⠫⠶⠶⠕⠕"] # 0x21db FIX: how to represent triple shafts? + - "⇜": [t: "⠫⠪⠢⠤⠔⠒⠢"] # 0x21dc + - "⇝": [t: "⠫⠢⠤⠔⠒⠢⠕"] # 0x21dd + - "⇞": [t: "⠳⠳⠈⠫⠣⠒⠒⠕⠻"] # 0x21de + - "⇟": [t: "⠳⠳⠈⠫⠩⠒⠒⠕⠻"] # 0x21df + - "⇠": [t: "⠫⠪⠒⠒"] # 0x21e0 + - "⇡": [t: "⠫⠣⠒⠒⠕"] # 0x21e1 + - "⇢": [t: "⠫⠒⠒⠕"] # 0x21e2 + - "⇣": [t: "⠫⠩⠒⠒⠕"] # 0x21e3 + - "⇤": [t: "⠫⠳⠪⠒⠒"] # 0x21e4 + - "⇥": [t: "⠫⠒⠒⠕⠳"] # 0x21e5 + - "⇦": [t: "⠫⠸⠪⠒⠒"] # 0x21e6 + - "⇧": [t: "⠫⠣⠸⠒⠒⠕"] # 0x21e7 + - "⇨": [t: "⠫⠸⠒⠒⠕"] # 0x21e8 + - "⇩": [t: "⠫⠩⠸⠒⠒⠕"] # 0x21e9 + - "⇳": [t: "⠫⠣⠸⠪⠒⠒⠕"] # 0x21f3 + - "⇴": [t: "⠫⠒⠒⠕⠨⠡"] # 0x21f4 + - "⇵": [t: "⠫⠩⠒⠒⠕⠐⠫⠣⠒⠒⠕"] # 0x21f5 + - "⇶": [t: "⠫⠒⠒⠕⠫⠒⠒⠕⠫⠒⠒⠕"] # 0x21f6 + - "⇷": [t: "⠳⠈⠫⠪⠒⠒⠻"] # 0x21f7 + - "⇸": [t: "⠳⠈⠫⠒⠒⠕⠻"] # 0x21f8 + - "⇹": [t: "⠳⠈⠫⠪⠒⠒⠕"] # 0x21f9 + - "⇺": [t: "⠳⠳⠈⠫⠪⠒⠒⠻"] # 0x21fa + - "⇻": [t: "⠳⠳⠈⠫⠒⠒⠕⠻"] # 0x21fb + - "⇼": [t: "⠳⠳⠈⠫⠪⠒⠒⠕"] # 0x21fc + - "⇽": [t: "⠫⠳⠒⠒"] # 0x21fd + - "⇾": [t: "⠫⠒⠒⠳"] # 0x21fe + - "⇿": [t: "⠫⠳⠒⠒⠳"] # 0x21ff + + - "ⅆ": [t: "⠙"] # 0x2146 + - "ⅇ": [t: "⠑"] # 0x2147 + - "ⅈ": [t: "⠊"] # 0x2148 + - "⌜": [t: "⠈⠘⠷"] # 0x231c + - "⌝": [t: "⠈⠘⠾"] # 0x231d + - "⌞": [t: "⠈⠰⠷"] # 0x231e + - "⌟": [t: "⠈⠰⠾"] # 0x231f + - "〈": [t: "⠨⠨⠷"] # 0x2329 + - "〉": [t: "⠨⠨⠾"] # 0x232a + - "⏞": [t: "⠨⠷"] # 0x23de + - "⏟": [t: "⠨⠾"] # 0x23df + - "⎴": [t: "⠈⠷"] # 0x23b4 + - "⎵": [t: "⠈⠾"] # 0x23b5 + - "⟪": [t: "⠨⠨⠨⠷"] # 0x27ea + - "⟫": [t: "⠨⠨⠨⠾"] # 0x27eb + - "〈": [t: "⠨⠨⠷"] # 0x3008 + - "〉": [t: "⠨⠨⠾"] # 0x3009 + - "《": [t: "⠨⠨⠨⠷"] # 0x300a + - "》": [t: "⠨⠨⠨⠾"] # 0x300b + - "「": [t: "⠈⠘⠷"] # 0x300c + - "」": [t: "⠈⠘⠾"] # 0x300d + - "↼": [t: "⠫⠈⠪⠒⠒"] # 0x21bc + - "↽": [t: "⠫⠠⠪⠒⠒"] # 0x21bd + - "↾": [t: "⠫⠣⠒⠒⠠⠕"] # 0x21be + - "↿": [t: "⠫⠣⠒⠒⠈⠕"] # 0x21bf + - "⇀": [t: "⠫⠒⠒⠈⠕"] # 0x21c0 + - "⇁": [t: "⠫⠒⠒⠠⠕"] # 0x21c1 + - "⇂": [t: "⠫⠪⠒⠒⠈⠕"] # 0x21c2 + - "⇃": [t: "⠫⠩⠒⠒⠠⠕"] # 0x21c3 + - "⇋": # 0x21cb + + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠒⠈⠕"] + - "⇌": # 0x21cc + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + + - "℔": [t: "⠳"] # 0x2114 + - "™": [t: "⠘⠞"] # 0x2122 + - "Å": [t: "⠈⠠⠁"] # 0x212b + - "­": [t: "⠤"] # 0xad + - " -​": [t: "W"] # 0x2000 - 0x200b (various spaces / ZWSP) + - "
": [t: "W"] # 0x2028 + - "
": [t: "W"] # 0x2029 + - " ": [t: "W"] # 0x202f + - " ": [t: "W"] # 0x205f + - "₣": [t: "⠈⠋"] # 0x20A3 (French franc sign + - "₦": [t: "⠈⠝"] # 0x2046 (Naira sign) + - "₩": [t: "⠈⠺"] # 0x20A9 (Won sign) + - "€": [t: "⠈⠑"] # 0x20AC (Euro sign) + - "": [t: "W"] # 0xfeff + - "☆": [t: "⠫⠎"] # 0x2606 + - "✕": [t: "⠈⠡"] # 0x2715 + - "¼": [t: "⠹⠂⠌⠲⠼"] # 0xbc + - "½": [t: "⠹⠂⠌⠆⠼"] # 0xbd + - "¾": [t: "⠹⠒⠌⠲⠼"] # 0xbe + - "⅐": [t: "⠹⠂⠌⠶⠼"] # 0x2150 + - "⅑": [t: "⠹⠂⠌⠔⠼"] # 0x2151 + - "⅒": [t: "⠹⠂⠌⠂⠴⠼"] # 0x2152 + - "⅓": [t: "⠹⠂⠌⠒⠼"] # 0x2153 + - "⅔": [t: "⠹⠆⠌⠒⠼"] # 0x2154 + - "⅕": [t: "⠹⠂⠌⠢⠼"] # 0x2155 + - "⅖": [t: "⠹⠆⠌⠢⠼"] # 0x2156 + - "⅗": [t: "⠹⠒⠌⠢⠼"] # 0x2157 + - "⅘": [t: "⠹⠲⠌⠢⠼"] # 0x2158 + - "⅙": [t: "⠹⠂⠌⠖⠼"] # 0x2159 + - "⅚": [t: "⠹⠢⠌⠖⠼"] # 0x215a + - "⅛": [t: "⠹⠂⠌⠦⠼"] # 0x215b + - "⅜": [t: "⠹⠒⠌⠦⠼"] # 0x215c + - "⅝": [t: "⠹⠢⠌⠦⠼"] # 0x215d + - "⅞": [t: "⠹⠶⠌⠦⠼"] # 0x215e + - "⅟": [t: "⠹⠂⠌⠼"] # 0x215f + - "↉": [t: "⠹⠴⠌⠒⠼"] # 0x2189 + - "㉈": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x3248 + - "㉉": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠆⠴⠻⠻"] # 0x3249 + - "㉊": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠒⠴⠻⠻"] # 0x324a + - "㉋": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠲⠴⠻⠻"] # 0x324b + - "㉌": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠢⠴⠻⠻"] # 0x324c + - "㉍": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠖⠴⠻⠻"] # 0x324d + - "㉎": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠶⠴⠻⠻"] # 0x324e + - "㉏": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠦⠴⠻⠻"] # 0x324f + + + - "‘": [t: "P⠠⠦"] # 0x2018 (Left single quotation mark) + - "’": [t: "P⠠⠴"] # 0x2019 (Right single quotation mark) + - "“": [t: "P⠦"] # 0x201C (Left double quotation mark) + - "”": [t: "P⠴"] # 0x201D (Right double quotation mark) + - "‰": [t: "`00"] + - "ℏ": [t: "⠈⠓"] # 0x210F (Planck constant over two pi) + - "ℓ": [t: "TE⠇"] # 0x2113 (Script small l (differs from 1d4c1: 4-56-123)) + - "℘": [tc: "TCL⠏"] # 0x2118 (Script Capital P) +# - "ℝ": [t: ""] # 0x211D (Double-struck capital R (no nemeth double struck)) +# - "ⅆ": [t: ""] # 0x2146 (Double-struck italic small d) +# - "ⅇ": [t: ""] # 0x2147 (Double-struck italic small e) +# - "⇚": [t: "⠀⠫⠪⠸⠸⠀"] # 0x21DA (Leftwards triple arrow) +# - "⇛": [t: "⠀⠫⠸⠸⠕⠀"] # 0x21DB (Rightwards triple arrow) + - "∔": [t: "⠐⠬⠣⠡⠻"] # 0x2214 (Dot plus) + - "√": [t: "⠜"] # 0x221A (Square root) + - "∛": [t: "⠣⠒⠜"] # 0x221B (Cube root) + - "∜": [t: "⠣⠲⠜"] # 0x221C (Fourth root) + - "∡": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2221 (Measured angle) + - "∹": [t: "⠤⠐⠂"] # 0x2239 (Excess) + - "∺": [t: "⠐⠤⠩⠡⠡⠣⠡⠡⠻"] # 0x223A (Geometric proportion) + - "∻": [t: "⠐⠈⠱⠩⠡⠣⠡⠻"] # 0x223B (Homothetic) + - "∽": [t: "⠠⠱"] # 0x223D (Reversed tilde) + - "≋": [t: "⠈⠱⠈⠱⠈⠱"] # 0x224B (Triple tilde) + - "≌": [t: "⠠⠱⠨⠅"] # 0x224C (All equal to) + - "≏": [t: "⠈⠣⠱"] # 0x224F (Difference between) + - "≔": [t: "⠐⠂⠨⠅"] # 0x2254 (Colon equals) + - "≕": [t: "⠨⠅⠐⠂"] # 0x2255 (Equals colon) + - "≖": [t: "⠀⠨⠡⠈⠨⠅⠻⠀"] # 0x2256 (Ring in equal to) + - "⊌": [t: "⠨⠬⠈⠫⠪⠒⠻"] # 0x228C (Multiset) + - "⊍": [t: "⠡⠈⠨⠬⠻"] # 0x228D (Multiset multiplication) + - "⊎": [t: "⠬⠈⠨⠬⠻"] # 0x228E (Multiset union) + - "⊕": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2295 (Circled plus) + - "⊖": [t: "⠫⠉⠸⠫⠤⠻"] # 0x2296 (Circled minus) + - "⊗": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2297 (Circled times) + - "⊘": [t: "⠫⠉⠸⠫⠸⠌⠻"] # 0x2298 (Circled division slash) + - "⊙": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2299 (Circled dot operator) + - "⊚": [t: "⠫⠉⠸⠫⠘⠨⠡⠻"] # 0x229a (Circled ring operator) + - "⊛": [t: "⠫⠉⠸⠫⠈⠼⠻"] # 0x229B (Circled asterisk operator) + - "⊜": [t: "⠫⠉⠸⠫⠨⠅⠻"] # 0x229C (Circled equals) + - "⊝": [t: "⠫⠉⠸⠫⠤⠤⠻"] # 0x229D (Circled dash) + - "⊞": [t: "⠫⠲⠸⠫⠬⠻"] # 0x229E (Squared plus) + - "⊟": [t: "⠫⠲⠸⠫⠤⠻"] # 0x229F (Squared minus) + - "⊠": [t: "⠫⠲⠸⠫⠈⠡⠻"] # 0x22A0 (Squared times) + - "⊡": [t: "⠫⠲⠸⠫⠡⠻"] # 0x22A1 (Squared dot operator) + - "⊢": [t: "⠫⠳⠒"] # 0x22A2 (Right tack) + - "⊣": [t: "⠫⠒⠳"] # 0x22A3 (Left tack) + - "⊤": [t: "⠫⠩⠳⠒"] # 0x22A4 (Down tack) +# - "⊥": [t: "⠀⠫⠏⠀ or ⠀⠫⠣⠳⠒⠀"] # 0x22A5 (Up tack (See also 27C2)) +# - "⊦": [t: ""] # 0x22A6 (Assertion) +# - "⊨": [t: ""] # 0x22A8 (TRUE) +# - "⊲": [t: ""] # 0x22B2 (Normal subgroup of) +# - "⊳": [t: ""] # 0x22B3 (Contains as normal subgroup) +# - "⊴": [t: ""] # 0x22B4 (Normal subgroup of or equal to) +# - "⊵": [t: ""] # 0x22B5 (Contains as normal subgroup or equal to) + - "⊶": [t: "⠫⠨⠡⠒⠡"] # 0x22B6 (Original of) + - "⊷": [t: "⠫⠡⠒⠨⠡"] # 0x22B7 (Image of) + - "⊸": [t: "⠫⠒⠨⠡"] # 0x22B8 (Multimap) +# - "⊾": [t: ""] # 0x22BE (Right angle w arc) + - "⊿": [t: "⠫⠞⠨⠗⠻"] # 0x22BF (Right triangle) + - "⋅": [t: "⠡"] # 0x22C5 (Dot operator) + - "∙": [t: "⠡"] # 0x2219 -- similar looking to 0x22c5, so using the same braille + - "⋆": [t: "⠫⠎"] # 0x22C6 (Star operator) + - "⋍": [t: "⠠⠱⠱"] # 0x22CD (Reversed tilde equals) + - "⋖": [t: "⠡⠈⠐⠅⠻"] # 0x22D6 (Less-than w dot) + - "⋗": [t: "⠡⠈⠨⠂⠻"] # 0x22D7 (Greater-than w dot) + - "⋘": [t: "⠐⠅⠈⠐⠅⠈⠐⠅⠻"] # 0x22D8 (Much less-than) + - "⋙": [t: "⠨⠂⠈⠨⠂⠈⠨⠂⠻"] # 0x22D9 (Much greater-than) + - "⋲": [t: "⠱⠈⠈⠑⠻"] # 0x22F2 (Element of w long horizontal stroke) + - "⋵": [t: "⠐⠈⠑⠣⠡⠻"] # 0x22F5 (Element of w dot above) + - "⋶": [t: "⠱⠈⠑"] # 0x22F6 (Element of w overbar) + - "⋺": [t: "⠱⠈⠈⠢⠻"] # 0x22FA (Contains w long horizontal stroke) + - "⋽": [t: "⠱⠈⠢"] # 0x22FD (Contains w overbar) + - "⌈": [t: "⠈⠘⠷"] # 0x2308 (Left ceiling) + - "⌉": [t: "⠈⠘⠾"] # 0x2309 (Right ceiling) + - "⌊": [t: "⠈⠰⠷"] # 0x230A (Left floor) + - "⌋": [t: "⠈⠰⠾"] # 0x230B (Right floor) + - "⌢": [t: "⠫⠁"] # 0x2322 (Frown) + - "⌣": [t: "⠫⠄"] # 0x2323 (Smile) + - "■": [t: "⠫⠸⠲"] # 0x25A0 (Filled square) + - "▫": [t: "⠫⠲"] # 0x25AB (Small white Square) + - "□": [t: "⠫⠲"] # 0x25A1 (Square) + - "◻": [t: "⠫⠲"] # 0x25FB (Square) + - "▬": [t: "⠫⠸⠗"] # 0x25AC (Filled rectangle) + - "▭": [t: "⠫⠗"] # 0x25AD (Rectangle) + - "▲": [t: "⠫⠸⠞"] # 0x25B2 (Black up-pointing triangle) + - "△": [t: "⠫⠞"] # 0x25B3 (White up-pointing triangle) + - "▼": [t: "⠸⠨⠫"] # 0x25BC (Black down-pointing triangle) + - "○": [t: "⠫⠉"] # 0x25CB (White circle) + - "●": [t: "⠫⠸⠉"] # 0x25CF (Black circle) + - "◫": [t: "⠫⠲⠸⠫⠳⠻"] # 0x25EB (White square w vertical bisecting line) +# - "⊥": [t: "⠫⠏"] # 0x27C2 (Perpendicular) + - "⟃": [t: "⠨⠡⠈⠸⠐⠅⠻"] # 0x27C3 (Open subset) + - "⟄": [t: "⠨⠡⠈⠸⠨⠂⠻"] # 0x27C4 (Open superset) + - "⟜": [t: "⠫⠨⠡⠒⠒"] # 0x27DC (Left multimap) + - "⟝": [t: "⠫⠳⠒⠒"] # 0x27DD (Long right tack) + - "⟞": [t: "⠫⠒⠒⠳"] # 0x27DE (Long left tack) + - "⟟": [t: "⠫⠣⠳⠒⠒⠨⠡"] # 0x27DF (Up tack w circle above) + - "⟦": [t: "⠈⠸⠷"] # 0x27E6 (Mathematical left white square bracket) + - "⟧": [t: "⠈⠸⠾"] # 0x27E7 (Mathematical right white square bracket) + - "⟨": [t: "⠨⠨⠷"] # 0x27E8 (Mathematical left angle bracket) + - "⟩": [t: "⠨⠨⠾"] # 0x27E9 (Mathematical right angle bracket) + - "⟵": [t: "⠀⠫⠪⠒⠒⠒⠀"] # 0x27F5 (Long leftwards arrow ) + - "⟶": [t: "⠀⠫⠒⠒⠒⠕⠀"] # 0x27F6 (Long rightwards arrow) + - "⟷": [t: "⠀⠫⠪⠒⠒⠒⠕⠀"] # 0x27F7 (Long left right arrow) + - "⟸": [t: "⠀⠫⠪⠶⠶⠶⠀"] # 0x27F8 (Long leftwards arrow ) + - "⟹": [t: "⠀⠫⠶⠶⠶⠕⠀"] # 0x27F9 (Long rightwards arrow) + - "⟺": [t: "⠀⠫⠪⠶⠶⠶⠕⠀"] # 0x27FA (Long left right arrow) + - "⟻": [t: "⠀⠫⠪⠒⠒⠒⠳⠀"] # 0x27FB (Long leftwards arrow from bar) + - "⟼": [t: "⠀⠫⠳⠒⠒⠒⠕⠀"] # 0x27FC (Long rightwards arrow from bar) + - "⟽": [t: "⠀⠫⠪⠶⠶⠶⠳⠀"] # 0x27FD (Long leftwards double arrow from bar) + - "⟾": [t: "⠀⠫⠳⠶⠶⠶⠕⠀"] # 0x27FE (Long rightwards double arrow from bar) + - "⟿": [t: "⠀⠫⠢⠤⠔⠒⠢⠤⠔⠒⠢⠕⠀"] # 0x27FF (Long rightwards squiggle arrow) + - "⤡": [t: "⠀⠫⠘⠪⠒⠒⠕⠀"] # 0x2921 (North west and south east arrow) + - "⤢": [t: "⠀⠫⠰⠪⠒⠒⠕⠀"] # 0x2922 (North east and south west arrow) + - "🣐": # 0x1f8d0 (Long rightwards arrow over leftwards arrow (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣑": # 0x1f8d1 (Long rightwards harpoon over leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣒": # 0x1f8d2 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + - "🣓": # 0x1f8d3 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒⠒"] + else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣔": # 0x1f8d4 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] + - "🣕": # 0x1f8d5 (Short leftwards harpoon over long rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠫⠒⠒⠕"] + else: [t: "⠫⠈⠪⠒⠫⠒⠒⠠⠕"] + - "⥂": # 0x2942 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + - "⥄": # 0x2944 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒⠒"] + else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] + - "⥃": # 0x2943 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] + - "🣖": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d6 (long rightwards arrow with through X) + - "🣗": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d7 (long rightwards arrow with double slash) chem code says it is same as 0x1f8d6 + + + - "⦃": [t: "⠨⠸⠷"] # 0x2983 (Left white curly bracket) + - "⦄": [t: "⠨⠸⠾"] # 0x2984 (Right white curly bracket) + - "⦑": [t: "⠡⠈⠨⠨⠷⠻"] # 0x2991 (Left angle bracket w dot) + - "⦒": [t: "⠡⠈⠨⠨⠾⠻"] # 0x2992 (Right angle bracket w dot) + - "⧄": [t: "⠫⠲⠸⠫⠔⠻"] # 0x29C4 (Square rising diagonal slash) + - "⧅": [t: "⠫⠲⠸⠫⠢⠻"] # 0x29C5 (Square falling diagonal slash) + - "⧆": [t: "⠫⠲⠸⠫⠈⠼⠻"] # 0x29C6 (Squared asterisk) + - "⧊": [t: "⠐⠫⠞⠣⠡⠻"] # 0x29CA (Triangle w dot above) + - "⧋": [t: "⠫⠞⠱"] # 0x29CB (Triangle w underbar) + - "⧌": [t: "⠫⠞⠎"] # 0x29CC (s in triangle §114) + - "⧦": [t: "⠫⠳⠶⠶⠳"] # 0x29E6 (Gleich stark) + - "⨌": [t: "⠮⠮⠮⠮"] # 0x2A0C (Quadruple integral operator) + - "⨍": [t: "⠮⠈⠱⠻"] # 0x2A0D (Finite part integral) + - "⨎": [t: "⠮⠈⠱⠱⠻"] # 0x2A0E (Integral w double stroke) + - "⨖": [t: "⠮⠈⠫⠲⠻"] # 0x2A16 (Quaternion integral operator) + - "⨘": [t: "⠮⠈⠈⠡⠻"] # 0x2A18 (Integral w times sign) + - "⨙": [t: "⠮⠈⠨⠩⠻"] # 0x2A19 (Integral w intersection) + - "⨚": [t: "⠮⠈⠨⠬⠻"] # 0x2A1A (Integral w union) + - "⨛": [t: "⠣⠮"] # 0x2A1B (Integral w overbar (upper)) + - "⨜": [t: "⠩⠮"] # 0x2A1C (Integral w underbar (lower)) + - "⨢": [t: "⠐⠬⠣⠨⠡⠻"] # 0x2A22 (Plus sign w small circle above) + - "⨣": [t: "⠐⠬⠣⠸⠣⠻"] # 0x2A23 (Plus sign w circumflex accent above) + - "⨤": [t: "⠐⠬⠣⠈⠱⠻"] # 0x2A24 (Plus sign w tilde above) + - "⨥": [t: "⠐⠬⠩⠡⠻"] # 0x2A25 (Plus sign w dot below) + - "⨪": [t: "⠐⠱⠩⠡⠻"] # 0x2A2A (Minus sign w dot below) + - "⨰": [t: "⠐⠈⠡⠣⠡⠻"] # 0x2A30 (Multiplication sign w dot above) + - "⨱": [t: "⠈⠡⠱"] # 0x2A31 (Multiplication sign w underbar) + - "⨸": [t: "⠫⠉⠸⠫⠨⠌⠻"] # 0x2A38 (Circled division sign) + - "⨹": [t: "⠫⠞⠸⠫⠬⠻"] # 0x2A39 (Plus sign in triangle) + - "⨺": [t: "⠫⠞⠸⠫⠤⠻"] # 0x2A3A (Minus sign in triangle) + - "⨻": [t: "⠫⠞⠸⠫⠈⠡⠻"] # 0x2A3B (Multiplication sign in triangle) + - "⩀": [t: "⠡⠈⠨⠩⠻"] # 0x2A40 (Intersection w dot) + - "⩁": [t: "⠤⠈⠨⠬⠻"] # 0x2A41 (Union w minus sign) + - "⩂": [t: "⠱⠨⠬"] # 0x2A42 (Union w overbar) + - "⩃": [t: "⠱⠨⠩"] # 0x2A43 (Intersection w overbar) + - "⩑": [t: "⠐⠈⠩⠣⠡⠻"] # 0x2A51 (Logical AND w dot above) + - "⩒": [t: "⠐⠈⠬⠣⠡⠻"] # 0x2A52 (Logical OR w dot above) + - "⩜": [t: "⠱⠈⠈⠩⠻"] # 0x2A5C (Logical AND with horizontal dash) + - "⩝": [t: "⠱⠈⠈⠬⠻"] # 0x2A5D (Logical OR with horizontal dash) + - "⩞": [t: "⠱⠱⠈⠩"] # 0x2A5E (Logical AND w double overbar) + - "⩟": [t: "⠈⠩⠱"] # 0x2A5F (Logical AND w underbar) + - "⩠": [t: "⠈⠩⠱⠱"] # 0x2A60 (Logical AND w double underbar) + - "⩢": [t: "⠱⠱⠈⠬"] # 0x2A62 (Logical OR w double overbar) + - "⩣": [t: "⠈⠬⠱⠱"] # 0x2A63 (Logical OR w double underbar) + - "⩦": [t: "⠐⠨⠅⠩⠡⠻"] # 0x2A66 (Equals sign w dot below) + - "⩧": [t: "⠐⠸⠇⠣⠡⠻"] # 0x2A67 (Identical w dot above) + - "⩪": [t: "⠐⠈⠱⠣⠡⠻"] # 0x2A6A (Tilde operator w dot above) + - "⩬": [t: "⠈⠱⠱⠈⠱"] # 0x2A6C (Similar minus similar) + - "⩭": [t: "⠐⠈⠱⠨⠅⠣⠡⠻"] # 0x2A6D (Congruent w dot above) + - "⩮": [t: "⠐⠨⠅⠣⠈⠼⠻"] # 0x2A6E (Equals w asterisk) + - "⩯": [t: "⠐⠈⠱⠈⠱⠣⠸⠣⠻"] # 0x2A6F (Almost equal to w circumflex) + - "⩰": [t: "⠈⠱⠈⠱⠨⠅"] # 0x2A70 (Approximately equal or equal to) + - "⩱": [t: "⠨⠅⠬"] # 0x2A71 (Equals sign above plus sign) + - "⩲": [t: "⠬⠨⠅"] # 0x2A72 (Plus sign above equals sign) + - "⩳": [t: "⠨⠅⠈⠱"] # 0x2A73 (Equals sign above tilde operator) + - "⩴": [t: "⠐⠂⠐⠂⠨⠅"] # 0x2A74 (Double colon equal) + - "⩵": [t: "⠨⠅⠨⠅"] # 0x2A75 (Two consecutive equals signs) + - "⩶": [t: "⠨⠅⠨⠅⠨⠅"] # 0x2A76 (Three consecutive equals signs) + - "⩷": [t: "⠐⠨⠅⠩⠡⠡⠣⠡⠡⠻"] # 0x2A77 (Equals sign w two dots above and two dots below) + - "⩸": [t: "⠐⠸⠇⠣⠡⠡⠡⠡⠻"] # 0x2A78 (Equivalent w four dots above) + - "⩹": [t: "⠐⠅⠈⠨⠡⠻"] # 0x2A79 (Less than w circle inside) + - "⩺": [t: "⠨⠂⠈⠨⠡⠻"] # 0x2A7A (Greater than w circle inside) + - "⩻": [t: "⠐⠐⠅⠣⠸⠦⠻"] # 0x2A7B (Less-than w question mark above) + - "⩼": [t: "⠐⠨⠂⠣⠸⠦⠻"] # 0x2A7C (Greater-than w question mark above) + - "⪅": [t: "⠐⠅⠈⠱⠈⠱"] # 0x2A85 (Less-than or approximate) + - "⪆": [t: "⠨⠂⠈⠱⠈⠱"] # 0x2A86 (Greater-than or approximate) + - "⪋": [t: "⠐⠅⠨⠅⠨⠂"] # 0x2A8B (Less-than above double-line equal above greater-than) + - "⪌": [t: "⠨⠂⠨⠅⠐⠅"] # 0x2A8C (Greater-than above double-line equal above less-than) + - "⪍": [t: "⠐⠅⠈⠱⠱"] # 0x2A8D (Less-than above similar or equal to) + - "⪎": [t: "⠨⠂⠈⠱⠱"] # 0x2A8E (Greater-than above similar or equal to) + - "⪏": [t: "⠐⠅⠈⠱⠨⠂"] # 0x2A8F (Less-than above similar above greater-than) + - "⪐": [t: "⠨⠂⠈⠱⠐⠅"] # 0x2A90 (Greater-than above similar above less-than) + - "⪑": [t: "⠐⠅⠨⠂⠨⠅"] # 0x2A91 (Less-than above greater-than above double-line equal) + - "⪒": [t: "⠨⠂⠐⠅⠨⠅"] # 0x2A92 (Greater-than above less-than above double-line equal) + - "⪙": [t: "⠨⠅⠐⠅"] # 0x2A99 (Double-line equal to or less-than) + - "⪚": [t: "⠨⠅⠨⠂"] # 0x2A9A (Double-line equal to or greater-than) + - "⪝": [t: "⠈⠱⠐⠅"] # 0x2A9D (Similar or less-than) + - "⪞": [t: "⠈⠱⠨⠂"] # 0x2A9E (Similar or greater-than) + - "⪟": [t: "⠈⠱⠐⠅⠨⠅"] # 0x2A9F (Similar above less-than above equals sign) + - "⪠": [t: "⠈⠱⠨⠂⠨⠅"] # 0x2AA0 (Similar above greater-than above equals sign) + - "⪮": [t: "⠈⠣⠨⠅"] # 0x2AAE (Equals sign with bumpy above) + - "⪽": [t: "⠡⠈⠸⠐⠅⠻"] # 0x2ABD (Subset w dot) + - "⪾": [t: "⠡⠈⠸⠨⠂⠻"] # 0x2ABE (Superset w dot) + - "⪿": [t: "⠐⠸⠐⠅⠩⠬⠻"] # 0x2ABF (Subset with plus sign below) + - "⫀": [t: "⠐⠸⠨⠂⠩⠬⠻"] # 0x2AC0 (Superset with plus sign below) + - "⫁": [t: "⠐⠸⠐⠅⠩⠈⠡⠻"] # 0x2AC1 (Subset with multiplication sign below) + - "⫂": [t: "⠐⠸⠨⠂⠩⠈⠡⠻"] # 0x2AC2 (Superset with multiplication sign below) + - "⫅": [t: "⠸⠐⠅⠨⠅"] # 0x2AC5 (Subset of above equals sign) + - "⫆": [t: "⠸⠨⠂⠨⠅"] # 0x2AC6 (Superset of above equals sign) + - "⫇": [t: "⠸⠐⠅⠈⠱"] # 0x2AC7 (Subset of above tilde operator) + - "⫈": [t: "⠸⠨⠂⠈⠱"] # 0x2AC8 (Superset of above tilde operator) + - "⫉": [t: "⠸⠐⠅⠈⠱⠈⠱"] # 0x2AC9 (Subset of above almost equal to) + - "⫊": [t: "⠸⠨⠂⠈⠱⠈⠱"] # 0x2ACA (Superset of above almost equal to) + - "⫋": [t: "⠸⠐⠅⠌⠨⠅"] # 0x2ACB (Subset of above not equals sign) + - "⫌": [t: "⠸⠨⠂⠌⠨⠅"] # 0x2ACC (Superset of above not equals sign) + - "⫓": [t: "⠸⠐⠅⠸⠨⠂"] # 0x2AD3 (Subset above superset) + - "⫔": [t: "⠸⠨⠂⠸⠐⠅"] # 0x2AD4 (Superset above subset) + - "⫕": [t: "⠸⠐⠅⠸⠐⠅"] # 0x2AD5 (Subset above subset) + - "⫖": [t: "⠸⠨⠂⠸⠨⠂"] # 0x2AD6 (Superset above superset) + - "⫗": [t: "⠸⠨⠂⠐⠸⠐⠅"] # 0x2AD7 (Superset beside subset) + - "⫯": [t: "⠫⠩⠨⠡⠒⠒"] # 0x2AEF (Vertical line w circle above) + - "⫰": [t: "⠫⠣⠨⠡⠒⠒"] # 0x2AF0 (Vertical line w circle below) + - "⫱": [t: "⠫⠣⠨⠡⠒⠒⠳"] # 0x2AF1 (Down tack w circle below) + - "⫲": [t: "⠱⠈⠫⠇⠻"] # 0x2AF2 (Parallel w horizontal stroke) + - "⫳": [t: "⠈⠱⠈⠫⠇⠻"] # 0x2AF3 (Parallel w tilde operator) + - "⫴": [t: "⠳⠳⠳"] # 0x2AF4 (Triple vertical bar binary relation) + - "⬬": [t: "⠫⠸⠑"] # 0x2B2C (Black ellipse) + - "⬭": [t: "⠫⠑"] # 0x2B2D (White ellipse) + - "⬰": [t: "⠨⠡⠈⠫⠪⠒⠒⠻"] # 0x2B30 (Left arrow w small circle) + - "⬱": [t: "⠫⠪⠒⠒⠫⠪⠒⠒⠫⠪⠒⠒"] # 0x2B31 (Three leftwards arrows) + - "⬲": [t: "⠫⠪⠒⠒⠈⠫⠉⠸⠫⠬⠻"] # 0x2B32 (Left arrow w circled plus) + - "⮖": [t: "⠨⠡"] # 0x2B96 (Standard State Symbol) + - "⯃": [t: "⠫⠸⠦"] # 0x2BC3 (Horizontal black octagon) + + # SRE lists this as a custom transcription + - "⋊": [t: "⠈⠡⠳"] # 0x22ca (RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT) diff --git a/Rules/Braille/UEB/UEB_Rules.yaml b/Rules/Braille/UEB/UEB_Rules.yaml index 912e03d8a..c161530dd 100644 --- a/Rules/Braille/UEB/UEB_Rules.yaml +++ b/Rules/Braille/UEB/UEB_Rules.yaml @@ -8,7 +8,7 @@ variables: [MatchingWhitespace: "true()"] replace: - test: - - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-previous-space-width >= 0.25" # BANA 5.3(a) + - if: "(@class='MathML-unit' or contains(@data-intent-property, ':unit:') or BaseNode(.)[@class='MathML-unit' or contains(@data-intent-property, ':unit:')]) and @data-previous-space-width >= 0.25" # treat spaced units as separate exprs for G1 placement then: [t: "𝐖"] - else_if: "@data-previous-space-width > 1.1" then: [t: "⠬"] @@ -16,7 +16,7 @@ then: [t: "W"] - x: "." - test: - - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-following-space-width >= 0.25" # BANA 5.3(a) + - if: "(@class='MathML-unit' or contains(@data-intent-property, ':unit:') or BaseNode(.)[@class='MathML-unit' or contains(@data-intent-property, ':unit:')]) and @data-following-space-width >= 0.25" # treat spaced units as separate exprs for G1 placement then: [t: "𝐖"] - else_if: "@data-following-space-width > 1.1" then: [t: "⠬"] @@ -107,7 +107,7 @@ replace: - t: "1⠷" - x: "*[1]" - - t: "1⠨⠌" + - t: "⠨⠌" - x: "*[2]" - t: "1⠾" @@ -256,9 +256,10 @@ tag: mo # add spaces around comparison operators unless they are in a script position, variables: # ratio not spaced in UEB; also make sure it is an infix operator (e.g., "~" can be prefix) + # ∴ / ∵ / ∎ are spaced like comparison signs (GTM 1.7.9 "∴ force = …"; GTM 11) - AddSpaces: "$UseSpacesAroundAllOperators or ( parent::*[self::m:mrow] and $NewScriptContext='' and - .!='∶' and IsInDefinition(., 'Braille', 'NemethComparisonOperators') )" + .!='∶' and (IsInDefinition(., 'Braille', 'NemethComparisonOperators') or .='∴' or .='∵' or .='∎') )" match: "." replace: - test: diff --git a/Rules/prefs.yaml b/Rules/prefs.yaml index 8a324721e..6c36c4020 100644 --- a/Rules/prefs.yaml +++ b/Rules/prefs.yaml @@ -61,6 +61,7 @@ UEB: StartMode: "Grade2" # Grade1/Grade2 -- assumed starting mode UEB braille (Grade1 assumes we are in G1 passage mode) + # Grade 1 indicators follow ICEB GTM 1.7 (also BANA 2026 math/science guidance) # UEB Guide to Technical Material (https://iceb.org/Guidelines_for_Technical_Material_2008-10.pdf) # says to normally treat Fraktur and DoubleStruck as Script diff --git a/docs/users.md b/docs/users.md index 6328e522f..a5ab487d3 100644 --- a/docs/users.md +++ b/docs/users.md @@ -10,8 +10,6 @@ MathCAT is a tool used together with a screen reader. Through MathCAT, you can h In screen readers NVDA and JAWS, MathCAT comes built-in. This means you don’t have to install it as an addon. -MathCAT is still available as an addon to NVDA. You can use it if you want access to the very latest features between NVDA releases. [How to use MathCAT as an addon is described here](#how-to-install-mathcat-as-an-addon-in-nvda). - ## Get started with MathCAT Use your screen reader on a web page or in an e-book as usual. When you get to a mathematical expression, the screen reader will read it out automatically. If you want to investigate the expression more closely you can activate the navigation mode by pressing Space. In NVDA, the NVDA-key+Alt+M can also be used. If you want to leave the navigation mode, press Escape. @@ -139,42 +137,4 @@ Note: tabular math means mathematical content in a table structure such as a mat ## Do you have feedback on MathCAT? -MathCAT is under active development, and we want your feedback. Do you have a feature request, or have you found any bugs? Please create an issue on [MathCAT's GitHub page](https://github.com/daisy/MathCAT/issues). You are also welcome to get in touch by signing up to [the mailing list of the DAISY MathCAT working group](https://www.surveymonkey.com/r/HXTLXGN). - -## How to install MathCAT as an addon in NVDA - -To use MathCAT as an add-on, do the following: - -1. Open the NVDA menu. -2. Choose Tools and then Add-on store. -3. In the add-on store, choose Available add-ons. - - The channel settings have the following options: - - Stable: Will only search for stable add-ons - - All: Will search for all add-ons - - Beta: Will search for add-ons that are in beta - - Dev: Will search for add-ons in development - - Choose option "All". - -4. Search for MathCAT and install. - -5. In the settings menu, you can control how add-ons are updated: - - - Open the NVDA menu - - Choose settings and Add-on Store - - For Automatic updates, you can choose between: - - Notify - - Update Automatically - - Deactivated - - For Default update channel, you have the following options: - - - Same: If automatic updates is set to “Update Automatically” and your add-on is a beta, it will update to the first available beta. - - Any: It will update to any version of the add-on. - - Do not update - - Stable: It will only update to stable versions. - - Beta or dev: It will update to beta or dev versions. - - Beta: It will update to beta versions. - - Dev: It will update to dev versions. \ No newline at end of file +MathCAT is under active development, and we want your feedback. Do you have a feature request, or have you found any bugs? Please create an issue on [MathCAT's GitHub page](https://github.com/daisy/MathCAT/issues). You are also welcome to get in touch by signing up to [the mailing list of the DAISY MathCAT working group](https://www.surveymonkey.com/r/HXTLXGN). \ No newline at end of file diff --git a/src/braille.rs b/src/braille.rs index 1966f09c0..4f0ac0ec7 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -15,6 +15,7 @@ use crate::canonicalize::get_parent; use std::borrow::Cow; use std::ops::Range; use std::sync::LazyLock; +#[allow(unused_imports)] use log::{debug, error}; fn is_ueb_prefix(ch: char) -> bool { @@ -1047,11 +1048,9 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str let use_only_grade1 = pref_manager.pref_to_string("UEB_START_MODE").as_str() == "Grade1"; - // '𝐖' is a hard break -- basically, it separates exprs - let mut result = result.split('𝐖') - .map(|str| pick_start_mode(str, use_only_grade1) + "W") - .collect::(); - result.pop(); // we added a 'W' at the end that needs to be removed. + // 𝐖 is a unit hard-break. For GTM 1.7 it is still a symbols-sequence boundary (same as W), + // so G1 passage decisions (1.7.3(b) / 1.7.5(c)) see the whole expression. + let result = pick_start_mode(&result, use_only_grade1); let result = result.replace("tW", "W"); @@ -1067,42 +1066,42 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str return result.to_string(); fn pick_start_mode(raw_braille: &str, use_only_grade1: bool) -> String { - // Need to decide what the start mode should be - // From http://www.brailleauthority.org/ueb/ueb_math_guidance/final_for_posting_ueb_math_guidance_may_2019_102419.pdf - // Unless a math expression can be correctly represented with only a grade 1 symbol indicator in the first three cells - // or before a single letter standing alone anywhere in the expression, - // begin the expression with a grade 1 word indicator (or a passage indicator if the expression includes spaces) - // Apparently "only a grade 1 symbol..." means at most one grade 1 symbol based on some examples (GTM 6.4, example 4) + // Decide grade 1 indicator placement per ICEB GTM 1.7 (2025). + // BANA's 2026 guidance adopts the same GTM §1.7 rules: + // https://www.brailleauthority.org/sites/default/files/2026-07/Guidance%20on%20Transcribing%20Math%20and%20Science%20in%20UEB%202026.pdf + // GTM 1.7.3 decides indicators per *symbols-sequence* (RUEB 2.1 -- text separated by spaces): + // (a) allow one grade 1 symbol per symbols-sequence, or a grade 1 word indicator if a sequence needs more than one; + // (b) use a grade 1 passage only if three or more symbols-sequences each need a grade 1 symbol or word indicator. + // Grade 1 symbol indicators forced by 'a-j' following a digit are not counted (GTM 1.7.3 note). // debug!("before determining mode: '{}'", raw_braille); - // a bit ugly because we need to store the string if we have cap passage mode - let raw_braille_string = if is_cap_passage_mode_good(raw_braille) {convert_to_cap_passage_mode(raw_braille)} else {String::default()}; - let raw_braille = if raw_braille_string.is_empty() {raw_braille} else {&raw_braille_string}; - if use_only_grade1 { - return remove_unneeded_mode_changes(raw_braille, UEB_Mode::Grade1, UEB_Duration::Passage); - } - let grade2 = remove_unneeded_mode_changes(raw_braille, UEB_Mode::Grade2, UEB_Duration::Symbol); - debug!("Symbol mode: '{}'", grade2); - - if is_grade2_string_ok(&grade2) { - return grade2; + // Capital passage (RUEB §8.5): like word mode's extra 'C' (CC…), passage uses CCC…Ce. + // Count G1 on the original (with capital markers) so standing-alone letters still force + // passage when needed (BANA Ex 5-15), but apply mode changes after stripping C/𝐶 so + // Grade 2 contractions are not blocked by cap_word_mode (chem "ch" → ⠡). + let had_unit_break = raw_braille.contains('𝐖'); + let raw_braille = raw_braille.replace('𝐖', "W"); + let use_cap_passage = is_cap_passage_mode_good(&raw_braille); + let stripped_caps = raw_braille.replace(['C', '𝐶'], ""); + let apply_to = if use_cap_passage { stripped_caps.as_str() } else { raw_braille.as_str() }; + let result = if use_only_grade1 { + remove_unneeded_mode_changes(apply_to, UEB_Mode::Grade1, UEB_Duration::Passage) } else { - // BANA says use g1 word mode if spaces are present, but that's not what their examples do - // A conversation with Ms. DeAndrea from BANA said that they mean use passage mode if ≥3 "segments" (≥2 blanks) - // The G1 Word mode might not be at the start (iceb.rs:omission_3_6_7) - let grade1_word = try_grade1_word_mode(raw_braille); - debug!("Word mode: '{}'", grade1_word); - if !grade1_word.is_empty() { - return grade1_word; - } else { - let grade1_passage = remove_unneeded_mode_changes(raw_braille, UEB_Mode::Grade1, UEB_Duration::Passage); - return "⠰⠰⠰".to_string() + &grade1_passage + "⠰⠄"; - } + gtm_1_7_mode(&raw_braille, apply_to, had_unit_break) + }; + if use_cap_passage { + return convert_to_cap_passage_mode(&result); } + return result; - /// Return true if at least five (= # of cap passage indicators) cap indicators and no lower case letters + /// Return true if capital passage mode should be used (RUEB §8.5 / BANA Ex 5-15). + /// Requires no lowercase letters, and either ≥5 capital indicators (chem / dense caps) + /// or ≥3 letter-bearing symbols-sequences that are fully capitalized. + /// Grade 1 / numeric / other non-letter markers are transparent (do not abort the scan). fn is_cap_passage_mode_good(braille: &str) -> bool { let mut n_caps = 0; + let mut n_letter_seqs = 0; + let mut seq_has_letter = false; let mut is_cap_mode = false; let mut cap_mode = UEB_Duration::Symbol; // real value set when is_cap_mode is set to true let mut chars = braille.chars(); @@ -1115,6 +1114,7 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str if !is_cap_mode { return false; } + seq_has_letter = true; chars.next(); // skip letter if cap_mode == UEB_Duration::Symbol { is_cap_mode = false; @@ -1130,76 +1130,33 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str } n_caps += 1; } else if ch == 'W' || ch == '𝐖' { - if is_cap_mode { - assert!(cap_mode == UEB_Duration::Word); + if seq_has_letter { + n_letter_seqs += 1; + seq_has_letter = false; } is_cap_mode = false; - } else if ch == '1' && is_cap_mode { - break; } + // else: '1', '𝟙', 'N', operators, typeforms, etc. — transparent } - return n_caps > 4; + if seq_has_letter { + n_letter_seqs += 1; + } + return n_caps >= 5 || n_letter_seqs >= 3; } + /// After G1 placement: strip any leftover capital markers and wrap with intermediate + /// `CCC`…`Ce` (same map as word mode: C→⠠, e→⠄ → ⠠⠠⠠…⠠⠄). Applied after + /// remove_unneeded_mode_changes so parsers never treat passage opener as CL letters. + /// If G1 passage is present (⠰⠰⠰…⠰⠄), capital passage goes inside (BANA Ex 5-15). fn convert_to_cap_passage_mode(braille: &str) -> String { - return "⠠⠠⠠".to_string() + &braille.replace(['C', '𝐶'], "") + "⠠⠄"; - } - - /// Return true if the BANA or ICEB guidelines say it is ok to start with grade 2 - fn is_grade2_string_ok(grade2_braille: &str) -> bool { - // BANA says use grade 2 if there is not more than one grade one symbol or single letter standing alone. - // The exact quote from their guidance: - // Unless a math expression can be correctly represented with only a grade 1 symbol indicator in the first three cells - // or before a single letter standing alone anywhere in the expression, - // begin the expression with a grade 1 word indicator - // Note: I modified this slightly to exclude the cap indicator in the count. That allows three more ICEB rule to pass and seems - // like it is a reasonable thing to do. - // Another modification is allow a single G1 indicator to occur after whitespace later on - // because ICEB examples show it and it seems better than going to passage mode if it is the only G1 indicator - - // Because of the 'L's which go away, we have to put a little more work into finding the first three chars - let chars = grade2_braille.chars().collect::>(); - let mut n_real_chars = 0; // actually number of chars - let mut found_g1 = false; - let mut i = 0; - while i < chars.len() { - let ch = chars[i]; - if ch == '1' && !is_forced_grade1(&chars, i) { - if found_g1 { - return false; - } - found_g1 = true; - } else if !"𝐶CLobc".contains(ch) { - if n_real_chars == 2 { - i += 1; - break; // this is the third real char - }; - n_real_chars += 1; - } - i += 1 - } - - // if we find *another* g1 that isn't forced and isn't standing alone, we are done - // I've added a 'follows whitespace' clause for test iceb.rs:omission_3_6_2 to the standing alone rule - // we only allow one standing alone example -- not sure if BANA guidance has this limit, but GTM 11_5_5_3 seems better with it - // Same for GTM 1_7_3_1 (passage mode is mentioned also) - let mut is_standing_alone_already_encountered = false; - let mut is_after_whitespace = false; - while i < chars.len() { - let ch = chars[i]; - if ch == 'W' { - is_after_whitespace = true; - } else if ch == '1' && !is_forced_grade1(&chars, i) { - if is_standing_alone_already_encountered || - ((found_g1 || !is_after_whitespace) && !is_single_letter_on_right(&chars, i)) { - return false; - } - found_g1 = true; - is_standing_alone_already_encountered = true; + let body = braille.replace(['C', '𝐶'], ""); + const G1_START: &str = "⠰⠰⠰"; + const G1_END: &str = "⠰⠄"; + if let Some(rest) = body.strip_prefix(G1_START) + && let Some(mid) = rest.strip_suffix(G1_END) { + return format!("{G1_START}CCC{mid}Ce{G1_END}"); } - i += 1; - } - return true; + return format!("CCC{body}Ce"); } /// Return true if the sequence of chars forces a '1' at the `i`th position @@ -1221,56 +1178,139 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str return false; } - fn is_single_letter_on_right(chars: &[char], i: usize) -> bool { - fn is_skip_char(ch: char) -> bool { - matches!(ch, 'B' | 'I' | '𝔹' | 'S' | 'T' | 'D' | 'C' | '𝐶' | 's' | 'w') - } + /// Count the number of non-forced grade 1 indicators needed for a single symbols-sequence. + /// (Forced indicators -- 'a-j' following a digit -- are excluded per the GTM 1.7.3 note.) + fn grade1_count(raw_word: &str) -> usize { + let grade2 = remove_unneeded_mode_changes(raw_word, UEB_Mode::Grade2, UEB_Duration::Symbol); + let chars = grade2.chars().collect::>(); + return chars.iter().enumerate() + .filter(|&(i, &ch)| ch == '1' && !is_forced_grade1(&chars, i)) + .count(); + } - // find the first char (if any) - let mut count = 0; // how many letters - let mut i = i+1; + /// True if this symbols-sequence contains a literary English word (4+ letters). + /// GTM 1.7.5: short math names (sin, cos, lim, min, …) and single-letter variables + /// are not treated as words. + fn sequence_contains_word(raw_word: &str) -> bool { + let chars: Vec = raw_word.chars().collect(); + let mut run = 0usize; + let mut i = 0usize; while i < chars.len() { let ch = chars[i]; - if !is_skip_char(ch) { - if ch == 'L' { - if count == 1 { - return false; // found a second letter in the sequence - } - count += 1; - } else { - return count==1; + if ch == 'L' { + run += 1; + i += 1; + if i < chars.len() { + i += 1; // skip the braille cell after 'L' + } + if run >= 4 { + return true; } - i += 2; // eat 'L' and actual letter + } else if ch == 'A' { + run += 1; + i += 1; + if run >= 4 { + return true; + } + } else if is_math_alphabet_typeform(ch) { + // Double-struck/script/fraktur/sans-serif letters are not part of an English word. + let end = math_typeform_item_end(&chars, i); + i = if end > i { end } else { i + 1 }; + run = 0; + } else if matches!(ch, 'C' | '𝐶' | 'G' | 'V' | 'B' | 'I' | 's' | 'w' | 'e') { + i += 1; // letter prefixes / literary typeform — do not break the run } else { + run = 0; i += 1; } } - return true; + false + } + + /// G2 form of a general-fraction sequence when two symbol indicators keep words + /// contracted (GTM 1.7.5(a)). `None` if more than two G1 symbols would remain. + fn g2_two_symbol_form(raw_word: &str) -> Option { + // Only the GTM 1.7.5(a) case: a general fraction whose numerator/denominator + // contain literary words. Superscripts, radicals, etc. keep 1.7.3 word indicators. + if !(raw_word.contains('⠷') && raw_word.contains("⠨⠌")) { + return None; + } + let g2 = remove_unneeded_mode_changes(raw_word, UEB_Mode::Grade2, UEB_Duration::Symbol); + let chars: Vec = g2.chars().collect(); + let n_g1 = chars.iter().enumerate() + .filter(|&(i, &ch)| ch == '1' && !is_forced_grade1(&chars, i)) + .count(); + if n_g1 <= 2 { + Some(g2) + } else { + None + } } - fn try_grade1_word_mode(raw_braille: &str) -> String { - // this isn't quite right, but pretty close -- try splitting at 'W' (words) - // only one of the parts can be in word mode and none of the others can have '1' unless forced - let mut g1_words = Vec::default(); - let mut found_word_mode = false; - for raw_word in raw_braille.split('W') { - let word = remove_unneeded_mode_changes(raw_word, UEB_Mode::Grade2, UEB_Duration::Symbol); - // debug!("try_grade1_word_mode: word='{}'", word); - let word_chars = word.chars().collect::>(); - let needs_word_mode = word_chars.iter().enumerate() - .any(|(i, &ch) | ch == '1' && !is_forced_grade1(&word_chars, i)); - if needs_word_mode { - if found_word_mode { - return "".to_string(); + /// ICEB GTM 1.7 grade 1 indicator placement (per symbols-sequence). + /// GTM 1.7.3: one grade 1 symbol per sequence, or a word indicator if more than one. + /// GTM 1.7.5(a): for a sequence containing word(s), allow two grade 1 symbol + /// indicators when that keeps the words in their usual contracted form + /// (e.g. ⠷…⠾ around work/distance). More than two still uses a word indicator. + /// `count_src` decides passage vs word vs symbol; changes are applied to `apply_to` + /// (may differ when capital markers were stripped for capital-passage mode). + fn gtm_1_7_mode(count_src: &str, apply_to: &str, had_unit_break: bool) -> String { + let count_words: Vec<&str> = count_src.split('W').collect(); + let apply_words: Vec<&str> = apply_to.split('W').collect(); + assert_eq!(count_words.len(), apply_words.len(), + "gtm_1_7_mode: count_src and apply_to must have the same number of symbols-sequences"); + + // Count how many symbols-sequences (whitespace-separated) need a non-forced grade 1 indicator. + let n_seq_needing = count_words.iter().filter(|raw_word| grade1_count(raw_word) >= 1).count(); + + // GTM 1.7.3(b) / 1.7.5(c): use a grade 1 passage if three or more sequences + // each need grade 1. 1.7.5(c) starts the passage at the first such sequence so + // preceding words (e.g. "speed") stay contracted. That delayed start is used when + // spaced units (𝐖) joined the expression; otherwise a passage wraps the whole + // expression (chem, roots, labelled equations). + if n_seq_needing >= 3 { + let first = count_words.iter().position(|w| grade1_count(w) >= 1) + .expect("n_seq_needing >= 3 implies a sequence that needs grade 1"); + let delay_start = had_unit_break && first > 0; + if delay_start { + let prefix: Vec = apply_words[..first].iter() + .map(|w| remove_unneeded_mode_changes(w, UEB_Mode::Grade2, UEB_Duration::Symbol)) + .collect(); + let rest = apply_words[first..].join("W"); + let passage = remove_unneeded_mode_changes(&rest, UEB_Mode::Grade1, UEB_Duration::Passage); + let mut out = prefix.join("W"); + if !out.is_empty() { + out.push('W'); } - found_word_mode = true; - g1_words.push("⠰⠰".to_string() + &remove_unneeded_mode_changes(raw_word, UEB_Mode::Grade1, UEB_Duration::Word) - ); - } else { - g1_words.push(word); + out.push_str("⠰⠰⠰"); + out.push_str(&passage); + out.push_str("⠰⠄"); + return out; } + let grade1_passage = remove_unneeded_mode_changes(apply_to, UEB_Mode::Grade1, UEB_Duration::Passage); + return "⠰⠰⠰".to_string() + &grade1_passage + "⠰⠄"; } - return if found_word_mode {g1_words.join("W")} else {"".to_string()}; + + // GTM 1.7.3(a): per sequence, allow one grade 1 symbol indicator, or a grade 1 word + // indicator if the sequence needs more than one. + let words = count_words.iter().zip(apply_words.iter()) + .map(|(&count_word, &apply_word)| { + let n_g1 = grade1_count(count_word); + if n_g1 >= 2 { + // GTM 1.7.5(a): two symbol indicators if that preserves contractions + if sequence_contains_word(count_word) + && let Some(g2) = g2_two_symbol_form(apply_word) { + g2 + } else { + "⠰⠰".to_string() + &remove_unneeded_mode_changes(apply_word, UEB_Mode::Grade1, UEB_Duration::Word) + } + } else { + // 0 or 1 grade 1 indicators: the grade 2 form leaves a single symbol indicator inline + remove_unneeded_mode_changes(apply_word, UEB_Mode::Grade2, UEB_Duration::Symbol) + } + }) + .collect::>(); + return words.join("W"); } } } @@ -1510,6 +1550,93 @@ fn is_left_intervening_char(ch: char) -> bool { matches!(ch, 'B' | 'I' | '𝔹' | 'S' | 'T' | 'D' | 'C' | '𝐶' | 's' | 'w') } +/// Double-struck / script / fraktur / sans-serif: math alphabets, not literary emphasis. +fn is_math_alphabet_typeform(ch: char) -> bool { + matches!(ch, '𝔹' | 'T' | 'D' | 'S') +} + +/// Advance past one letter with optional capital, Greek, and accent prefixes (`C? G|V? A…? L cell`). +fn skip_one_prefixed_letter(chars: &[char], mut i: usize) -> usize { + if i >= chars.len() { + return i; + } + if matches!(chars[i], 'C' | '𝐶') { + i += 1; + } + if i < chars.len() && matches!(chars[i], 'G' | 'V') { + i += 1; + } + if i < chars.len() && chars[i] == 'A' { + match index_after_accent_to_l(chars, i + 1) { + Some(i_l) => i = i_l, + None => return i, + } + } + if i < chars.len() && chars[i] == 'L' { + i += 1; + if i < chars.len() { + i += 1; // braille cell + } + } + i +} + +/// End index of a math-alphabet typeform item starting at `i` (`𝔹`/`T`/`D`/`S`). +/// `s` covers one letter; `w` covers the typeform word until its terminator. +fn math_typeform_item_end(chars: &[char], i: usize) -> usize { + if i >= chars.len() || !is_math_alphabet_typeform(chars[i]) { + return i; + } + let typeform = chars[i]; + let mut j = i + 1; + let word_mode = j < chars.len() && chars[j] == 'w'; + if j < chars.len() && matches!(chars[j], 's' | 'w') { + j += 1; + } + if word_mode { + loop { + let next = skip_one_prefixed_letter(chars, j); + if next == j { + break; + } + j = next; + } + if j + 1 < chars.len() && chars[j] == typeform && chars[j + 1] == 'e' { + j += 2; + } else if j < chars.len() && chars[j] == 'e' { + j += 1; + } + return j; + } + return skip_one_prefixed_letter(chars, j); +} + +/// RUEB 10.6.2: lower groupsigns "be", "con", "dis" only at the beginning of a word. +/// Beginning of a word = letters-sequence after space, hyphen or dash, optionally with +/// intervening punctuation/indicators from RUEB 2.6.2 (opening brackets/quotes, typeform, +/// capitals). Math operators and fraction indicators are not word boundaries. +/// `word_start` is the index of the first char of a grade-2 letter run ('L', 'C', or 'A'). +fn allows_lower_word_sign_at(chars: &[char], word_start: usize) -> bool { + if word_start >= chars.len() { + return false; + } + if word_start == 0 { + return true; + } + let mut j = word_start; + while j > 0 { + j -= 1; + let ch = chars[j]; + // 2.6.2 intervening symbols (opening punctuation flags, typeform, capitals, etc.) + if is_left_intervening_char(ch) || matches!(ch, 'e' | 'c' | 's' | 'w' | 'o' | 'b' | 'G' | 'V') { + continue; + } + // Must be space, hyphen or dash (RUEB 10.6.2 Note) + return "W𝐖-—―".contains(ch); + } + true +} + /// Return value for use_g1_word_mode() #[derive(Debug, PartialEq)] enum Grade1WordIndicator { @@ -1677,6 +1804,34 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d } // debug!("Grade 2: ch={}, duration: {:?}", ch, duration); match ch { + '𝔹' | 'T' | 'D' | 'S' => { + // Math-alphabet typeform: this letter is not part of an English word + // (GTM 11.6). Close any current contraction run, emit the typeform + // item on its own, then let following letters start a new word. + if let Some(start) = start_g2_letter { + if !cap_word_mode { + result = handle_contractions(&chars, start, i, result); + } + cap_word_mode = false; + start_g2_letter = None; + } + let unit_end = math_typeform_item_end(&chars, i); + // Typeform prefix (`𝔹s`) must precede the grade 1 indicator: ⠈⠆⠰⠠⠗, not ⠰⠈⠆⠠⠗. + let mut letter_start = i + 1; + if letter_start < unit_end && matches!(chars[letter_start], 's' | 'w') { + letter_start += 1; + } + result.extend(chars[i..letter_start].iter().copied()); + if let Some(letter_idx) = chars[i..unit_end].iter().position(|&c| c == 'L') { + let (is_alone, _, n_letters) = stands_alone(&chars, i + letter_idx); + if is_alone && n_letters == 1 { + result.push('1'); + mode = UEB_Mode::Grade1; + } + } + result.extend(chars[letter_start..unit_end].iter().copied()); + i = unit_end; + }, 'L' => { if start_g2_letter.is_none() { start_g2_letter = Some(i); @@ -1758,6 +1913,16 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d } }, '1' => { + // Contract the preceding letter run before consuming '1'. The '1' arm used + // to increment first, so handle_contractions saw "work1" and the whole-word + // patterns `^work$` / `^time$` failed (GTM 1.7.9 work/distance, 1.7.5(a) time). + if let Some(start) = start_g2_letter { + if !cap_word_mode { + result = handle_contractions(&chars, start, i, result); + } + cap_word_mode = false; + start_g2_letter = None; + } result.push(ch); i += 1; mode = UEB_Mode::Grade1; @@ -1778,7 +1943,7 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d _ => { if let Some(start) = start_g2_letter { if !cap_word_mode { - result = handle_contractions(&chars[start..i], result); + result = handle_contractions(&chars, start, i, result); } cap_word_mode = false; start_g2_letter = None; // not start of char sequence @@ -1794,7 +1959,7 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d } if mode != UEB_Mode::Grade2 && !cap_word_mode && let Some(start) = start_g2_letter { - result = handle_contractions(&chars[start..i], result); + result = handle_contractions(&chars, start, i, result); start_g2_letter = None; // not start of char sequence } }, @@ -1814,7 +1979,7 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d } if mode == UEB_Mode::Grade2 && let Some(start) = start_g2_letter { - result = handle_contractions(&chars[start..i], result); + result = handle_contractions(&chars, start, i, result); } return result; @@ -1946,10 +2111,12 @@ fn stands_alone(chars: &[char], i: usize) -> (bool, &[char], usize) { /// Return a modified result if chars can be contracted. /// Otherwise, the original string is returned -fn handle_contractions(chars: &[char], mut result: String) -> String { +fn handle_contractions(full_chars: &[char], start: usize, end: usize, mut result: String) -> String { struct Replacement { pattern: String, - replacement: &'static str + replacement: &'static str, + skip_if_word_in: Option<&'static phf::Set<&'static str>>, + word_start_only: bool, } const ASCII_TO_UNICODE: &[char] = &[ @@ -1968,47 +2135,95 @@ fn handle_contractions(chars: &[char], mut result: String) -> String { return unicode; } + // Whole words where the lower groupsign 'cc' must not apply (10.11.1 compound/function names). + static CC_EXCEPTION_WORDS: phf::Set<&str> = phf_set! { + "L⠁L⠗L⠉L⠉L⠕L⠎", // arccos + "L⠁L⠗L⠉L⠉L⠕L⠎L⠊L⠝L⠑", // arccosine + "L⠁L⠗L⠉L⠉L⠕L⠎L⠓", // arccosh + "L⠁L⠗L⠉L⠉L⠕L⠎L⠑L⠉", // arccosec + "L⠁L⠗L⠉L⠉L⠕L⠎L⠑L⠉L⠁L⠝L⠞", // arccosecant + "L⠁L⠗L⠉L⠉L⠕L⠞", // arccot + "L⠁L⠗L⠉L⠉L⠕L⠞L⠁L⠝L⠛L⠑L⠝L⠞", // arccotangent + "L⠁L⠗L⠉L⠉L⠎L⠉", // arccsc + }; + + // Words where the 'ea' lower groupsign must not apply (re- prefix morpheme boundary). + static EA_EXCEPTION_WORDS: phf::Set<&str> = phf_set! { + "L⠗L⠑L⠁L⠉L⠞L⠁L⠝L⠉L⠑", // reactance + "L⠗L⠑L⠁L⠉L⠞L⠊L⠕L⠝", // reaction + "L⠗L⠑L⠁L⠉L⠞L⠊L⠧L⠊L⠞L⠽", // reactivity + }; + + // Words where "be" is not the first syllable (RUEB 10.6.1 "But:" cases and similar). + static BE_EXCEPTION_WORDS: phf::Set<&str> = phf_set! { + "L⠃L⠑L⠁L⠗L⠊L⠝L⠛", // bearing + "L⠃L⠑L⠝L⠉L⠓L⠍L⠁L⠗L⠅", // benchmark + }; + // It would be much better from an extensibility point of view to read the table in from a file static CONTRACTIONS: LazyLock> = LazyLock::new(|| { vec![ + // 10.9: initial-letter (dot-5) wordsigns -- whole word only + Replacement{ pattern: format!("^{}$", to_unicode_braille("time")), replacement: "⠐⠞", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: format!("^{}$", to_unicode_braille("work")), replacement: "⠐⠺", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: format!("^{}$", to_unicode_braille("leverage")), replacement: "⠇⠐⠑⠁⠛⠑", skip_if_word_in: None, word_start_only: false }, + + // 10.7.1: dot-5 initial-letter contractions (as wordsigns / groupsigns) + Replacement{ pattern: format!("^{}", to_unicode_braille("through")), replacement: "⠐⠹", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: format!("(?PL.){}(?PL.)", to_unicode_braille("part")), replacement: "${s}⠐⠏${e}", skip_if_word_in: None, word_start_only: false }, + // 10.3: Strong contractions - Replacement{ pattern: to_unicode_braille("and"), replacement: "L⠯"}, - Replacement{ pattern: to_unicode_braille("for"), replacement: "L⠿"}, - Replacement{ pattern: to_unicode_braille("of"), replacement: "L⠷"}, - Replacement{ pattern: to_unicode_braille("the"), replacement: "L⠮"}, - Replacement{ pattern: to_unicode_braille("with"), replacement: "L⠾"}, + Replacement{ pattern: to_unicode_braille("and"), replacement: "L⠯", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("for"), replacement: "L⠿", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("of"), replacement: "L⠷", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("the"), replacement: "L⠮", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("with"), replacement: "L⠾", skip_if_word_in: None, word_start_only: false }, + + // 10.6.1-10.6.4 / 10.10.4: lower groupsigns be/con/dis -- only at word start, + // and preferred over other groupsigns (e.g. "distance" is ⠲⠞⠨⠑, not di+st+ance). + Replacement{ pattern: format!("^{}", to_unicode_braille("be")), replacement: "⠆", skip_if_word_in: Some(&BE_EXCEPTION_WORDS), word_start_only: true }, + Replacement{ pattern: format!("^{}", to_unicode_braille("con")), replacement: "⠒", skip_if_word_in: None, word_start_only: true }, + Replacement{ pattern: format!("^{}", to_unicode_braille("dis")), replacement: "⠲", skip_if_word_in: None, word_start_only: true }, - // 10.8: final-letter group signs (this need to precede 'en' and any other shorter contraction) - Replacement{ pattern: "(?PL.)L⠍L⠑L⠝L⠞".to_string(), replacement: "${s}L⠰L⠞" }, // ment - Replacement{ pattern: "(?PL.)L⠞L⠊L⠕L⠝".to_string(), replacement: "${s}L⠰L⠝" } ,// tion + // 10.8: final-letter group signs (these need to precede 'en' and any other shorter contraction) + Replacement{ pattern: "(?PL.)L⠍L⠑L⠝L⠞".to_string(), replacement: "${s}L⠰L⠞", skip_if_word_in: None, word_start_only: false }, // ment + Replacement{ pattern: "(?PL.)L⠞L⠊L⠕L⠝".to_string(), replacement: "${s}L⠰L⠝", skip_if_word_in: None, word_start_only: false }, // tion + Replacement{ pattern: "(?PL.)L⠊L⠞L⠽".to_string(), replacement: "${s}L⠰L⠽", skip_if_word_in: None, word_start_only: false }, // ity + Replacement{ pattern: "(?PL.)L⠁L⠝L⠉L⠑".to_string(), replacement: "${s}L⠨L⠑", skip_if_word_in: None, word_start_only: false }, // ance + Replacement{ pattern: "(?PL.)L⠎L⠊L⠕L⠝".to_string(), replacement: "${s}L⠨L⠝", skip_if_word_in: None, word_start_only: false }, // sion + Replacement{ pattern: "(?PL.)L⠑L⠝L⠉L⠑".to_string(), replacement: "${s}L⠰L⠑", skip_if_word_in: None, word_start_only: false }, // ence + Replacement{ pattern: "(?PL.)L⠝L⠑L⠎L⠎".to_string(), replacement: "${s}L⠰L⠎", skip_if_word_in: None, word_start_only: false }, // ness + Replacement{ pattern: "(?PL.)L⠕L⠥L⠝L⠙".to_string(), replacement: "${s}L⠨L⠙", skip_if_word_in: None, word_start_only: false }, // ound + Replacement{ pattern: "(?PL.)L⠕L⠥L⠝L⠞".to_string(), replacement: "${s}L⠨L⠞", skip_if_word_in: None, word_start_only: false }, // ount + Replacement{ pattern: "(?PL.)L⠇L⠑L⠎L⠎".to_string(), replacement: "${s}L⠨L⠎", skip_if_word_in: None, word_start_only: false }, // less + Replacement{ pattern: "(?PL.)L⠕L⠝L⠛".to_string(), replacement: "${s}L⠰L⠛", skip_if_word_in: None, word_start_only: false }, // ong + Replacement{ pattern: "(?PL.)L⠋L⠥L⠇".to_string(), replacement: "${s}L⠰L⠇", skip_if_word_in: None, word_start_only: false }, // ful // 10.4: Strong group signs - Replacement{ pattern: to_unicode_braille("ch"), replacement: "L⠡"}, - Replacement{ pattern: to_unicode_braille("gh"), replacement: "L⠣"}, - Replacement{ pattern: to_unicode_braille("sh"), replacement: "L⠩"}, - Replacement{ pattern: to_unicode_braille("th"), replacement: "L⠹"}, - Replacement{ pattern: to_unicode_braille("wh"), replacement: "L⠱"}, - Replacement{ pattern: to_unicode_braille("ed"), replacement: "L⠫"}, - Replacement{ pattern: to_unicode_braille("er"), replacement: "L⠻"}, - Replacement{ pattern: to_unicode_braille("ou"), replacement: "L⠳"}, - Replacement{ pattern: to_unicode_braille("ow"), replacement: "L⠪"}, - Replacement{ pattern: to_unicode_braille("st"), replacement: "L⠌"}, - Replacement{ pattern: "(?PL.)L⠊L⠝L⠛".to_string(), replacement: "${s}L⠬" }, // 'ing', not at start - Replacement{ pattern: to_unicode_braille("ar"), replacement: "L⠜"}, + Replacement{ pattern: to_unicode_braille("ch"), replacement: "L⠡", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("gh"), replacement: "L⠣", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("sh"), replacement: "L⠩", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("th"), replacement: "L⠹", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("wh"), replacement: "L⠱", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("ed"), replacement: "L⠫", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("er"), replacement: "L⠻", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("ou"), replacement: "L⠳", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("ow"), replacement: "L⠪", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("st"), replacement: "L⠌", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: "(?PL.)L⠊L⠝L⠛".to_string(), replacement: "${s}L⠬", skip_if_word_in: None, word_start_only: false }, // 'ing', not at start + Replacement{ pattern: to_unicode_braille("ar"), replacement: "L⠜", skip_if_word_in: None, word_start_only: false }, // 10.6.5: Lower group signs preceded and followed by letters // FIX: don't match if after/before a cap letter -- can't use negative pattern (?!...) in regex package - // Note: removed cc because "arccos" shouldn't be contracted (10.11.1), but there is no way to know about compound words - // Add it back after implementing a lookup dictionary of exceptions - Replacement{ pattern: "(?PL.)L⠑L⠁(?PL.)".to_string(), replacement: "${s}L⠂${e}" }, // ea - Replacement{ pattern: "(?PL.)L⠃L⠃(?PL.)".to_string(), replacement: "${s}L⠆${e}" }, // bb - // Replacement{ pattern: "(?PL.)L⠉L⠉(?PL.)".to_string(), replacement: "${s}L⠒${e}" }, // cc - Replacement{ pattern: "(?PL.)L⠋L⠋(?PL.)".to_string(), replacement: "${s}L⠖${e}" }, // ff - Replacement{ pattern: "(?PL.)L⠛L⠛(?PL.)".to_string(), replacement: "${s}L⠶${e}" }, // gg - - // 10.6.8: Lower group signs ("in" also 10.5.4 lower word signs) + Replacement{ pattern: "(?PL.)L⠑L⠁(?PL.)".to_string(), replacement: "${s}L⠂${e}", skip_if_word_in: Some(&EA_EXCEPTION_WORDS), word_start_only: false }, // ea + Replacement{ pattern: "(?PL.)L⠃L⠃(?PL.)".to_string(), replacement: "${s}L⠆${e}", skip_if_word_in: None, word_start_only: false }, // bb + Replacement{ pattern: "(?PL.)L⠉L⠉(?PL.)".to_string(), replacement: "${s}L⠒${e}", skip_if_word_in: Some(&CC_EXCEPTION_WORDS), word_start_only: false }, // cc + Replacement{ pattern: "(?PL.)L⠋L⠋(?PL.)".to_string(), replacement: "${s}L⠖${e}", skip_if_word_in: None, word_start_only: false }, // ff + Replacement{ pattern: "(?PL.)L⠛L⠛(?PL.)".to_string(), replacement: "${s}L⠶${e}", skip_if_word_in: None, word_start_only: false }, // gg + + // 10.6.8: lower group signs; also 10.6.2 word signs when at word start ("sin", etc.) // FIX: these need restrictions about only applying when upper dots are present - Replacement{ pattern: to_unicode_braille("en"), replacement: "⠢"}, - Replacement{ pattern: to_unicode_braille("in"), replacement: "⠔"}, + Replacement{ pattern: to_unicode_braille("en"), replacement: "⠢", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("in"), replacement: "⠔", skip_if_word_in: None, word_start_only: false }, ] }); @@ -2016,11 +2231,28 @@ fn handle_contractions(chars: &[char], mut result: String) -> String { static CONTRACTION_PATTERNS: LazyLock = LazyLock::new(|| init_patterns(&CONTRACTIONS)); static CONTRACTION_REGEX: LazyLock> = LazyLock::new(|| init_regex(&CONTRACTIONS)); + let word_start_ok = allows_lower_word_sign_at(full_chars, start); + let chars = &full_chars[start..end]; + // A non-English letter glued to an English word (GTM 1.7.9 Δtime, or a + // double-struck/script/fraktur letter) is not part of the English + // letters-sequence; contract the following word on its own (time → ⠐⠞). + let skip = leading_non_word_letter_len(chars); + if skip > 0 && skip < chars.len() { + return handle_contractions(full_chars, start + skip, end, result); + } let mut chars_as_str = chars.iter().collect::(); + let original_chars_as_str = chars_as_str.clone(); // debug!(" handle_contractions: examine '{}'", &chars_as_str); let matches = CONTRACTION_PATTERNS.matches(&chars_as_str); for i in matches.iter() { let element = &CONTRACTIONS[i]; + if let Some(exceptions) = element.skip_if_word_in + && exceptions.contains(&original_chars_as_str) { + continue; + } + if element.word_start_only && !word_start_ok { + continue; + } // debug!(" replacing '{}' with '{}' in '{}'", element.pattern, element.replacement, &chars_as_str); result.truncate(result.len() - chars_as_str.len()); chars_as_str = CONTRACTION_REGEX[i].replace_all(&chars_as_str, element.replacement).to_string(); @@ -2031,6 +2263,31 @@ fn handle_contractions(chars: &[char], mut result: String) -> String { + fn leading_non_word_letter_len(chars: &[char]) -> usize { + if chars.is_empty() { + return 0; + } + if is_math_alphabet_typeform(chars[0]) { + let end = math_typeform_item_end(chars, 0); + return if end < chars.len() { end } else { 0 }; + } + let mut i = 0; + if i < chars.len() && matches!(chars[i], 'C' | '𝐶') { + i += 1; + } + if i < chars.len() && matches!(chars[i], 'G' | 'V') { + i += 1; + if i < chars.len() && chars[i] == 'L' { + i += 1; + if i < chars.len() { + i += 1; // braille cell + } + return i; + } + } + 0 + } + fn init_patterns(contractions: &[Replacement]) -> RegexSet { let mut vec: Vec<&str> = Vec::with_capacity(contractions.len()); for contraction in contractions { @@ -3808,6 +4065,26 @@ mod tests { }); } + #[test] + fn ueb_lower_word_sign_contractions() -> Result<()> { + return braille_test(|| { + init_braille_mathml("distance")?; + set_preference("BrailleCode", "UEB")?; + let distance = get_braille("")?; + // RUEB 10.6.1 / 10.10.4: distance at word start uses dis lower groupsign + assert_eq!(distance, "⠲⠞⠨⠑", "dis lower groupsign at word start: got '{distance}'"); + // After fraction open (not a RUEB 10.6.2 word start), dis must not apply; st+ance may. + init_braille_mathml("distancetime")?; + let frac = get_braille("")?; + assert!(!frac.contains("⠲"), "dis wrongly used after fraction open: got '{frac}'"); + assert!(frac.contains("⠙⠊⠌⠨⠑") || frac.contains("⠙⠊⠎⠞⠁⠝⠉⠑"), + "expected st+ance or uncontracted distance after fraction open: got '{frac}'"); + init_braille_mathml("include")?; + assert!(get_braille("")?.starts_with("⠔"), "in lower groupsign at word start"); + return Ok(()); + }); + } + #[test] // This test probably should be repeated for each braille code and be taken out of here fn find_mathml_from_braille() -> Result<()> { @@ -3868,7 +4145,7 @@ mod tests { set_preference("BrailleCode", "UEB")?; let _braille = get_braille("")?; - let answers= &[0, 0, 0, 2, 3, 3, 3, 3, 4, 7, 7, 8, 9, 9, 10, 13, 12, 14, 14, 15, 15, 17, 17, 19, 19, 21, 10, 4, 4, 23, 23, 25, 25, 4, 0, 0]; + let answers= &[2, 2, 3, 3, 3, 3, 4, 4, 4, 7, 7, 8, 9, 9, 10, 13, 12, 14, 14, 15, 15, 17, 17, 19, 19, 21, 10, 4, 4, 23, 23, 25, 25, 4]; let answers = answers.map(|num| format!("id-{}", num)); debug!("\n\n*** Testing UEB ***"); for (i, answer) in answers.iter().enumerate() { @@ -3927,4 +4204,367 @@ mod tests { "oL⠈N⠹⠜L⠁cL⠈N⠩⠜L⠃" ); } + + /// Letter-to-Unicode-braille mapping (matches handle_contractions). + const TEST_ASCII_TO_UNICODE: &[char] = &[ + '⠀', '⠮', '⠐', '⠼', '⠫', '⠩', '⠯', '⠄', '⠷', '⠾', '⠡', '⠬', '⠠', '⠤', '⠨', '⠌', + '⠴', '⠂', '⠆', '⠒', '⠲', '⠢', '⠖', '⠶', '⠦', '⠔', '⠱', '⠰', '⠣', '⠿', '⠜', '⠹', + '⠈', '⠁', '⠃', '⠉', '⠙', '⠑', '⠋', '⠛', '⠓', '⠊', '⠚', '⠅', '⠇', '⠍', '⠝', '⠕', + '⠏', '⠟', '⠗', '⠎', '⠞', '⠥', '⠧', '⠺', '⠭', '⠽', '⠵', '⠪', '⠳', '⠻', '⠘', '⠸', + ]; + + fn ascii_to_uncontracted_braille(word: &str) -> String { + word.bytes() + .map(|b| TEST_ASCII_TO_UNICODE[(b.to_ascii_uppercase() - 32) as usize]) + .collect() + } + + /// Run handle_contractions on a lowercase ASCII word (L-cell input) and return Unicode braille. + fn contract_word(word: &str) -> String { + let l_chars: Vec = word.bytes() + .flat_map(|b| { + let ch = TEST_ASCII_TO_UNICODE[(b.to_ascii_uppercase() - 32) as usize]; + ['L', ch] + }) + .collect(); + let input: String = l_chars.iter().collect(); + let output = handle_contractions(&l_chars, 0, l_chars.len(), input); + output.chars().filter(|&c| c != 'L').collect() + } + + /// Grade-2 contraction coverage for technical vocabulary (311 words). + /// Cross-check goldens at https://brailletranslators.com/ (Grade 2 / UEB). + /// Should contract (225): force, work, time, speed, distance, area, height, weight, power, energy, density, gravity, acceleration, length, width, depth, cost, profit, interest, revenue, clearance, conductance, capacitance, resistance, impedance, reactance, variance, tolerance, luminosity, reliability, velocity, viscosity, precision, tension, efficiency, frequency, valence, stiffness, thickness, displacement, concentration, consumption, duration, elevation, fraction, friction, inflation, iteration, population, position, potential, production, resolution, rotation, charge, chord, pitch, growth, head, heat, reach, shear, margin, salary, offset, income, input, inventory, current, error, leverage, temperature, turnover, accuracy, breadth, strength, spread, discount, count, version, inductance, intensity, interval, perimeter, probability, utility, validity, parity, permeability, proportion, uncertainty, circumference, capacity, deceleration, dilution, enthalpy, entropy, equity, expense, gradient, headroom, inertia, latency, longitude, permittivity, period, principal, sensitivity, stress, throughput, thrust, wavelength, absorption, activation, activity, admittance, affinity, albedo, aperture, attenuation, average, bandwidth, baseline, bearing, benchmark, boiling, boundary, cohesion, compliance, compression, conductivity, coverage, damping, deflection, departure, depreciation, deviation, diffusion, dispersion, dissipation, distortion, divergence, downforce, elasticity, emissivity, emission, erosion, evaporation, expansion, expectation, extent, feedback, fidelity, filament, hardness, humidity, hysteresis, illuminance, incidence, index, inflow, influx, inhibition, injection, irradiance, kinetic, leakage, luminance, maturity, mean, median, melting, minimum, mobility, molarity, moisture, opacity, oscillation, outflow, overlap, parallax, parameter, peak, percentile, percentage, polarization, propagation, purity, quality, quantity, quenching, quotient, radiance, radiation, reaction, reactivity, redshift, reflectance, reflection, refraction, reluctance, remanence, resistivity, resonance, restitution, retardation, rigidity, salinity, saturation, scalar, scattering, separation, shrinkage, similarity, battery, diameter, epoch, gain, jitter, joule, momentum, shift, step, stride, zenith + /// Should not contract (86): mass, volume, base, rate, drag, drift, gap, lag, loss, pace, radius, range, ratio, risk, run, scale, size, slope, tax, torque, capital, impulse, latitude, magnitude, premium, pressure, response, return, trajectory, voltage, aspect, backlog, curvature, dosage, dose, equilibrium, excess, exposure, fatigue, hydraulic, lifetime, likelihood, maximum, metric, orbit, osmosis, pulse, quanta, quantum, regret, residual, ripple, root, rotor, sag, secant, signal, altitude, amplitude, angle, bias, bitrate, budget, bulk, decay, deficit, degree, delay, duty, lift, limit, load, modulus, payload, phase, price, sample, skew, slack, span, supply, total, value, vector, wage, yield + /// Note: arccos (cc exception, partial ar groupsign) is covered in tests/braille/UEB/other.rs contractions_1. + #[test] + fn ueb_technical_word_contractions() { + const SHOULD_CONTRACT: &[(&str, &str)] = &[ + ("force", "⠿⠉⠑"), + ("work", "⠐⠺"), + ("time", "⠐⠞"), + ("speed", "⠎⠏⠑⠫"), + ("distance", "⠲⠞⠨⠑"), + ("area", "⠜⠑⠁"), + ("height", "⠓⠑⠊⠣⠞"), + ("weight", "⠺⠑⠊⠣⠞"), + ("power", "⠏⠪⠻"), + ("energy", "⠢⠻⠛⠽"), + ("density", "⠙⠢⠎⠰⠽"), + ("gravity", "⠛⠗⠁⠧⠰⠽"), + ("acceleration", "⠁⠒⠑⠇⠻⠁⠰⠝"), + ("length", "⠇⠢⠛⠹"), + ("width", "⠺⠊⠙⠹"), + ("depth", "⠙⠑⠏⠹"), + ("cost", "⠉⠕⠌"), + ("profit", "⠏⠗⠷⠊⠞"), + ("interest", "⠔⠞⠻⠑⠌"), + ("revenue", "⠗⠑⠧⠢⠥⠑"), + ("clearance", "⠉⠇⠑⠜⠨⠑"), + ("conductance", "⠒⠙⠥⠉⠞⠨⠑"), + ("capacitance", "⠉⠁⠏⠁⠉⠊⠞⠨⠑"), + ("resistance", "⠗⠑⠎⠊⠌⠨⠑"), + ("impedance", "⠊⠍⠏⠫⠨⠑"), + ("reactance", "⠗⠑⠁⠉⠞⠨⠑"), + ("variance", "⠧⠜⠊⠨⠑"), + ("tolerance", "⠞⠕⠇⠻⠨⠑"), + ("luminosity", "⠇⠥⠍⠔⠕⠎⠰⠽"), + ("reliability", "⠗⠑⠇⠊⠁⠃⠊⠇⠰⠽"), + ("velocity", "⠧⠑⠇⠕⠉⠰⠽"), + ("viscosity", "⠧⠊⠎⠉⠕⠎⠰⠽"), + ("precision", "⠏⠗⠑⠉⠊⠨⠝"), + ("tension", "⠞⠢⠨⠝"), + ("efficiency", "⠑⠖⠊⠉⠊⠢⠉⠽"), + ("frequency", "⠋⠗⠑⠟⠥⠢⠉⠽"), + ("valence", "⠧⠁⠇⠰⠑"), + ("stiffness", "⠌⠊⠖⠰⠎"), + ("thickness", "⠹⠊⠉⠅⠰⠎"), + ("displacement", "⠲⠏⠇⠁⠉⠑⠰⠞"), + ("concentration", "⠒⠉⠢⠞⠗⠁⠰⠝"), + ("consumption", "⠒⠎⠥⠍⠏⠰⠝"), + ("duration", "⠙⠥⠗⠁⠰⠝"), + ("elevation", "⠑⠇⠑⠧⠁⠰⠝"), + ("fraction", "⠋⠗⠁⠉⠰⠝"), + ("friction", "⠋⠗⠊⠉⠰⠝"), + ("inflation", "⠔⠋⠇⠁⠰⠝"), + ("iteration", "⠊⠞⠻⠁⠰⠝"), + ("population", "⠏⠕⠏⠥⠇⠁⠰⠝"), + ("position", "⠏⠕⠎⠊⠰⠝"), + ("potential", "⠏⠕⠞⠢⠞⠊⠁⠇"), + ("production", "⠏⠗⠕⠙⠥⠉⠰⠝"), + ("resolution", "⠗⠑⠎⠕⠇⠥⠰⠝"), + ("rotation", "⠗⠕⠞⠁⠰⠝"), + ("charge", "⠡⠜⠛⠑"), + ("chord", "⠡⠕⠗⠙"), + ("pitch", "⠏⠊⠞⠡"), + ("growth", "⠛⠗⠪⠹"), + ("head", "⠓⠂⠙"), + ("heat", "⠓⠂⠞"), + ("reach", "⠗⠂⠡"), + ("shear", "⠩⠑⠜"), + ("margin", "⠍⠜⠛⠔"), + ("salary", "⠎⠁⠇⠜⠽"), + ("offset", "⠷⠋⠎⠑⠞"), + ("income", "⠔⠉⠕⠍⠑"), + ("input", "⠔⠏⠥⠞"), + ("inventory", "⠔⠧⠢⠞⠕⠗⠽"), + ("current", "⠉⠥⠗⠗⠢⠞"), + ("error", "⠻⠗⠕⠗"), + ("leverage", "⠇⠐⠑⠁⠛⠑"), + ("temperature", "⠞⠑⠍⠏⠻⠁⠞⠥⠗⠑"), + ("turnover", "⠞⠥⠗⠝⠕⠧⠻"), + ("accuracy", "⠁⠒⠥⠗⠁⠉⠽"), + ("breadth", "⠃⠗⠂⠙⠹"), + ("strength", "⠌⠗⠢⠛⠹"), + ("spread", "⠎⠏⠗⠂⠙"), + ("discount", "⠲⠉⠨⠞"), + ("count", "⠉⠨⠞"), + ("version", "⠧⠻⠨⠝"), + ("inductance", "⠔⠙⠥⠉⠞⠨⠑"), + ("intensity", "⠔⠞⠢⠎⠰⠽"), + ("interval", "⠔⠞⠻⠧⠁⠇"), + ("perimeter", "⠏⠻⠊⠍⠑⠞⠻"), + ("probability", "⠏⠗⠕⠃⠁⠃⠊⠇⠰⠽"), + ("utility", "⠥⠞⠊⠇⠰⠽"), + ("validity", "⠧⠁⠇⠊⠙⠰⠽"), + ("parity", "⠏⠜⠰⠽"), + ("permeability", "⠏⠻⠍⠂⠃⠊⠇⠰⠽"), + ("proportion", "⠏⠗⠕⠏⠕⠗⠰⠝"), + ("uncertainty", "⠥⠝⠉⠻⠞⠁⠔⠞⠽"), + ("circumference", "⠉⠊⠗⠉⠥⠍⠋⠻⠰⠑"), + ("capacity", "⠉⠁⠏⠁⠉⠰⠽"), + ("deceleration", "⠙⠑⠉⠑⠇⠻⠁⠰⠝"), + ("dilution", "⠙⠊⠇⠥⠰⠝"), + ("enthalpy", "⠢⠹⠁⠇⠏⠽"), + ("entropy", "⠢⠞⠗⠕⠏⠽"), + ("equity", "⠑⠟⠥⠰⠽"), + ("expense", "⠑⠭⠏⠢⠎⠑"), + ("gradient", "⠛⠗⠁⠙⠊⠢⠞"), + ("headroom", "⠓⠂⠙⠗⠕⠕⠍"), + ("inertia", "⠔⠻⠞⠊⠁"), + ("latency", "⠇⠁⠞⠢⠉⠽"), + ("longitude", "⠇⠰⠛⠊⠞⠥⠙⠑"), + ("permittivity", "⠏⠻⠍⠊⠞⠞⠊⠧⠰⠽"), + ("period", "⠏⠻⠊⠕⠙"), + ("principal", "⠏⠗⠔⠉⠊⠏⠁⠇"), + ("sensitivity", "⠎⠢⠎⠊⠞⠊⠧⠰⠽"), + ("stress", "⠌⠗⠑⠎⠎"), + ("throughput", "⠐⠹⠏⠥⠞"), + ("thrust", "⠹⠗⠥⠌"), + ("wavelength", "⠺⠁⠧⠑⠇⠢⠛⠹"), + ("absorption", "⠁⠃⠎⠕⠗⠏⠰⠝"), + ("activation", "⠁⠉⠞⠊⠧⠁⠰⠝"), + ("activity", "⠁⠉⠞⠊⠧⠰⠽"), + ("admittance", "⠁⠙⠍⠊⠞⠞⠨⠑"), + ("affinity", "⠁⠖⠔⠰⠽"), + ("albedo", "⠁⠇⠃⠫⠕"), + ("aperture", "⠁⠏⠻⠞⠥⠗⠑"), + ("attenuation", "⠁⠞⠞⠢⠥⠁⠰⠝"), + ("average", "⠁⠧⠻⠁⠛⠑"), + ("bandwidth", "⠃⠯⠺⠊⠙⠹"), + ("baseline", "⠃⠁⠎⠑⠇⠔⠑"), + ("bearing", "⠃⠑⠜⠬"), + ("benchmark", "⠃⠢⠡⠍⠜⠅"), + ("boiling", "⠃⠕⠊⠇⠬"), + ("boundary", "⠃⠨⠙⠜⠽"), + ("cohesion", "⠉⠕⠓⠑⠨⠝"), + ("compliance", "⠉⠕⠍⠏⠇⠊⠨⠑"), + ("compression", "⠉⠕⠍⠏⠗⠑⠎⠨⠝"), + ("conductivity", "⠒⠙⠥⠉⠞⠊⠧⠰⠽"), + ("coverage", "⠉⠕⠧⠻⠁⠛⠑"), + ("damping", "⠙⠁⠍⠏⠬"), + ("deflection", "⠙⠑⠋⠇⠑⠉⠰⠝"), + ("departure", "⠙⠑⠐⠏⠥⠗⠑"), + ("depreciation", "⠙⠑⠏⠗⠑⠉⠊⠁⠰⠝"), + ("deviation", "⠙⠑⠧⠊⠁⠰⠝"), + ("diffusion", "⠙⠊⠖⠥⠨⠝"), + ("dispersion", "⠲⠏⠻⠨⠝"), + ("dissipation", "⠲⠎⠊⠏⠁⠰⠝"), + ("distortion", "⠲⠞⠕⠗⠰⠝"), + ("divergence", "⠙⠊⠧⠻⠛⠰⠑"), + ("downforce", "⠙⠪⠝⠿⠉⠑"), + ("elasticity", "⠑⠇⠁⠌⠊⠉⠰⠽"), + ("emissivity", "⠑⠍⠊⠎⠎⠊⠧⠰⠽"), + ("emission", "⠑⠍⠊⠎⠨⠝"), + ("erosion", "⠻⠕⠨⠝"), + ("evaporation", "⠑⠧⠁⠏⠕⠗⠁⠰⠝"), + ("expansion", "⠑⠭⠏⠁⠝⠨⠝"), + ("expectation", "⠑⠭⠏⠑⠉⠞⠁⠰⠝"), + ("extent", "⠑⠭⠞⠢⠞"), + ("feedback", "⠋⠑⠫⠃⠁⠉⠅"), + ("fidelity", "⠋⠊⠙⠑⠇⠰⠽"), + ("filament", "⠋⠊⠇⠁⠰⠞"), + ("hardness", "⠓⠜⠙⠰⠎"), + ("humidity", "⠓⠥⠍⠊⠙⠰⠽"), + ("hysteresis", "⠓⠽⠌⠻⠑⠎⠊⠎"), + ("illuminance", "⠊⠇⠇⠥⠍⠔⠨⠑"), + ("incidence", "⠔⠉⠊⠙⠰⠑"), + ("index", "⠔⠙⠑⠭"), + ("inflow", "⠔⠋⠇⠪"), + ("influx", "⠔⠋⠇⠥⠭"), + ("inhibition", "⠔⠓⠊⠃⠊⠰⠝"), + ("injection", "⠔⠚⠑⠉⠰⠝"), + ("irradiance", "⠊⠗⠗⠁⠙⠊⠨⠑"), + ("kinetic", "⠅⠔⠑⠞⠊⠉"), + ("leakage", "⠇⠂⠅⠁⠛⠑"), + ("luminance", "⠇⠥⠍⠔⠨⠑"), + ("maturity", "⠍⠁⠞⠥⠗⠰⠽"), + ("mean", "⠍⠂⠝"), + ("median", "⠍⠫⠊⠁⠝"), + ("melting", "⠍⠑⠇⠞⠬"), + ("minimum", "⠍⠔⠊⠍⠥⠍"), + ("mobility", "⠍⠕⠃⠊⠇⠰⠽"), + ("molarity", "⠍⠕⠇⠜⠰⠽"), + ("moisture", "⠍⠕⠊⠌⠥⠗⠑"), + ("opacity", "⠕⠏⠁⠉⠰⠽"), + ("oscillation", "⠕⠎⠉⠊⠇⠇⠁⠰⠝"), + ("outflow", "⠳⠞⠋⠇⠪"), + ("overlap", "⠕⠧⠻⠇⠁⠏"), + ("parallax", "⠏⠜⠁⠇⠇⠁⠭"), + ("parameter", "⠏⠜⠁⠍⠑⠞⠻"), + ("peak", "⠏⠂⠅"), + ("percentile", "⠏⠻⠉⠢⠞⠊⠇⠑"), + ("percentage", "⠏⠻⠉⠢⠞⠁⠛⠑"), + ("polarization", "⠏⠕⠇⠜⠊⠵⠁⠰⠝"), + ("propagation", "⠏⠗⠕⠏⠁⠛⠁⠰⠝"), + ("purity", "⠏⠥⠗⠰⠽"), + ("quality", "⠟⠥⠁⠇⠰⠽"), + ("quantity", "⠟⠥⠁⠝⠞⠰⠽"), + ("quenching", "⠟⠥⠢⠡⠬"), + ("quotient", "⠟⠥⠕⠞⠊⠢⠞"), + ("radiance", "⠗⠁⠙⠊⠨⠑"), + ("radiation", "⠗⠁⠙⠊⠁⠰⠝"), + ("reaction", "⠗⠑⠁⠉⠰⠝"), + ("reactivity", "⠗⠑⠁⠉⠞⠊⠧⠰⠽"), + ("redshift", "⠗⠫⠩⠊⠋⠞"), + ("reflectance", "⠗⠑⠋⠇⠑⠉⠞⠨⠑"), + ("reflection", "⠗⠑⠋⠇⠑⠉⠰⠝"), + ("refraction", "⠗⠑⠋⠗⠁⠉⠰⠝"), + ("reluctance", "⠗⠑⠇⠥⠉⠞⠨⠑"), + ("remanence", "⠗⠑⠍⠁⠝⠰⠑"), + ("resistivity", "⠗⠑⠎⠊⠌⠊⠧⠰⠽"), + ("resonance", "⠗⠑⠎⠕⠝⠨⠑"), + ("restitution", "⠗⠑⠌⠊⠞⠥⠰⠝"), + ("retardation", "⠗⠑⠞⠜⠙⠁⠰⠝"), + ("rigidity", "⠗⠊⠛⠊⠙⠰⠽"), + ("salinity", "⠎⠁⠇⠔⠰⠽"), + ("saturation", "⠎⠁⠞⠥⠗⠁⠰⠝"), + ("scalar", "⠎⠉⠁⠇⠜"), + ("scattering", "⠎⠉⠁⠞⠞⠻⠬"), + ("separation", "⠎⠑⠏⠜⠁⠰⠝"), + ("shrinkage", "⠩⠗⠔⠅⠁⠛⠑"), + ("similarity", "⠎⠊⠍⠊⠇⠜⠰⠽"), + ("battery", "⠃⠁⠞⠞⠻⠽"), + ("diameter", "⠙⠊⠁⠍⠑⠞⠻"), + ("epoch", "⠑⠏⠕⠡"), + ("gain", "⠛⠁⠔"), + ("jitter", "⠚⠊⠞⠞⠻"), + ("joule", "⠚⠳⠇⠑"), + ("momentum", "⠍⠕⠰⠞⠥⠍"), + ("shift", "⠩⠊⠋⠞"), + ("step", "⠌⠑⠏"), + ("stride", "⠌⠗⠊⠙⠑"), + ("zenith", "⠵⠢⠊⠹"), + ]; + const SHOULD_NOT_CONTRACT: &[&str] = &[ + "mass", "volume", "base", "rate", "drag", "drift", "gap", "lag", "loss", "pace", + "radius", "range", "ratio", "risk", "run", "scale", "size", "slope", "tax", "torque", + "capital", "impulse", "latitude", "magnitude", "premium", "pressure", "response", "return", + "trajectory", "voltage", "aspect", "backlog", "curvature", "dosage", "dose", "equilibrium", + "excess", "exposure", "fatigue", "hydraulic", "lifetime", "likelihood", "maximum", "metric", + "orbit", "osmosis", "pulse", "quanta", "quantum", "regret", "residual", "ripple", "root", + "rotor", "sag", "secant", "signal", "altitude", "amplitude", "angle", "bias", "bitrate", + "budget", "bulk", "decay", "deficit", "degree", "delay", "duty", "lift", "limit", "load", + "modulus", "payload", "phase", "price", "sample", "skew", "slack", "span", "supply", + "total", "value", "vector", "wage", "yield", + ]; + + for &(word, expected) in SHOULD_CONTRACT { + let got = contract_word(word); + assert_eq!(expected, got, "word '{word}'"); + assert_ne!(ascii_to_uncontracted_braille(word), got, "word '{word}' should contract"); + } + for &word in SHOULD_NOT_CONTRACT { + let got = contract_word(word); + let uncontracted = ascii_to_uncontracted_braille(word); + assert_eq!(uncontracted, got, "word '{word}' should not contract"); + } + } + + #[test] + #[ignore] + fn ueb_technical_word_contractions_probe_batch2() { + let try_contract = [ + "circumference", "capacity", "deceleration", "dilution", "enthalpy", "entropy", "equity", "expense", + "gradient", "headroom", "inertia", "latency", "longitude", "permittivity", "period", "principal", + "sensitivity", "stress", "throughput", "thrust", "wavelength", "absorption", "activation", "activity", + "admittance", "affinity", "albedo", "aperture", "attenuation", "average", "bandwidth", "baseline", + "bearing", "benchmark", "boiling", "boundary", "cohesion", "compliance", "compression", "conductivity", + "coverage", "damping", "deflection", "departure", "depreciation", "deviation", "diffusion", "dispersion", + "dissipation", "distortion", "divergence", "downforce", "elasticity", "emissivity", "emission", "erosion", + "evaporation", "expansion", "expectation", "extent", "feedback", "fidelity", "filament", "hardness", + "humidity", "hysteresis", "illuminance", "incidence", "index", "inflow", "influx", "inhibition", + "injection", "irradiance", "kinetic", "leakage", "luminance", "maturity", "mean", "median", "melting", + "minimum", "mobility", "molarity", "moisture", "opacity", "oscillation", "outflow", "overlap", "parallax", + "parameter", "peak", "percentile", "percentage", "polarization", "propagation", "purity", "quality", + "quantity", "quenching", "quotient", "radiance", "radiation", "reaction", "reactivity", "redshift", + "reflectance", "reflection", "refraction", "reluctance", "remanence", "resistivity", "resonance", + "restitution", "retardation", "rigidity", "salinity", "saturation", "scalar", "scattering", "separation", + "shrinkage", "similarity", "battery", "diameter", "epoch", "gain", "jitter", "joule", "momentum", "shift", + "step", "stride", "zenith", + ]; + let try_not = [ + "capital", "impulse", "latitude", "magnitude", "premium", "pressure", "response", "return", "trajectory", + "voltage", "aspect", "backlog", "curvature", "dosage", "dose", "equilibrium", "excess", "exposure", + "fatigue", "hydraulic", "lifetime", "likelihood", "maximum", "metric", "orbit", "osmosis", "pulse", + "quanta", "quantum", "regret", "residual", "ripple", "root", "rotor", "sag", "secant", "signal", + "altitude", "amplitude", "angle", "bias", "bitrate", "budget", "bulk", "decay", "deficit", "degree", + "delay", "duty", "lift", "limit", "load", "modulus", "payload", "phase", "price", "sample", "skew", + "slack", "span", "supply", "total", "value", "vector", "wage", "yield", + ]; + let mut out = String::new(); + for w in try_contract { + let c = contract_word(w); + out.push_str(&format!("(\"{w}\", \"{c}\"),\n")); + } + for w in try_not { + out.push_str(&format!("// no: {w}\n")); + } + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("notes/_batch2_goldens.txt"); + std::fs::write(path, out).unwrap(); + } + + #[test] + #[ignore] + fn ueb_technical_word_contractions_probe() { + let should_contract = [ + "force", "work", "time", "speed", "distance", "area", "height", "weight", "power", "energy", + "density", "gravity", "acceleration", "length", "width", "depth", "cost", "profit", "interest", "revenue", + "clearance", "conductance", "capacitance", "resistance", "impedance", "reactance", "variance", "tolerance", + "luminosity", "reliability", "velocity", "viscosity", "precision", "tension", "efficiency", "frequency", + "valence", "stiffness", "thickness", "displacement", "concentration", "consumption", "duration", "elevation", + "fraction", "friction", "inflation", "iteration", "population", "position", "potential", "production", + "resolution", "rotation", "charge", "chord", "pitch", "growth", "head", "heat", "reach", "shear", "margin", + "salary", "offset", "income", "input", "inventory", "current", "error", "leverage", "temperature", + "turnover", "accuracy", "breadth", "strength", "spread", "discount", "count", "version", "inductance", + "intensity", "interval", "perimeter", "probability", "utility", "validity", "parity", "permeability", + "proportion", "uncertainty", + ]; + let should_not = [ + "mass", "volume", "base", "rate", "drag", "drift", "gap", "lag", "loss", "pace", + "radius", "range", "ratio", "risk", "run", "scale", "size", "slope", "tax", "torque", + ]; + eprintln!("=== SHOULD CONTRACT ==="); + for w in should_contract { + let c = contract_word(w); + let u = ascii_to_uncontracted_braille(w); + eprintln!("(\"{w}\", \"{c}\"), // uncontracted={u} changed={}", c != u); + } + eprintln!("=== SHOULD NOT CONTRACT ==="); + for w in should_not { + let c = contract_word(w); + let u = ascii_to_uncontracted_braille(w); + eprintln!("{w}: contracted={c} uncontracted={u} ok={}", c == u); + } + } } diff --git a/tests/braille/UEB/iceb.rs b/tests/braille/UEB/iceb.rs index 3bd48b19b..70eb878f8 100644 --- a/tests/braille/UEB/iceb.rs +++ b/tests/braille/UEB/iceb.rs @@ -1,7 +1,8 @@ // UEB tests for the basic mathml tags -// Initial tests are from BANA guidelines, mostly about initial chars for code switching -// http://www.brailleauthority.org/ueb/ueb_math_guidance/final_for_posting_ueb_math_guidance_may_2019_102419.pdf -// These tests start with "bana_" +// BANA guidance (2026; replaces 2019 provisional): +// https://www.brailleauthority.org/sites/default/files/2026-07/Guidance%20on%20Transcribing%20Math%20and%20Science%20in%20UEB%202026.pdf +// Older `bana_*` names map from 2019 section numbers where still valid; `bana2026_*` are new 2026 examples. +// Grade 1 indicators follow ICEB GTM §1.7 (adopted by BANA 2026). // // Many come from (refer to) https://iceb.org/guidelines_for_technical_material_2014.pdf // For example, "fraction_6_1_1" is a fraction example from section 6.1, and is the first example there. @@ -10,7 +11,8 @@ use anyhow::Result; #[test] fn bana_2_1() -> Result<()> { - let expr = "6=1×2×3 + // BANA 2026 Ex 2-1 (spacing of signs) + let expr = "6=1×2×3 =1+2+3"; test_braille("UEB", expr, "⠼⠋⠀⠐⠶⠀⠼⠁⠐⠦⠼⠃⠐⠦⠼⠉⠀⠐⠶⠀⠼⠁⠐⠖⠼⠃⠐⠖⠼⠉")?; return Ok(()); @@ -19,6 +21,7 @@ fn bana_2_1() -> Result<()> { #[test] fn bana_5_1() -> Result<()> { + // BANA 2026 Ex 5-1 let expr = "x+y=6"; test_braille("UEB", expr, "⠭⠐⠖⠽⠀⠐⠶⠀⠼⠋")?; return Ok(()); @@ -27,6 +30,7 @@ fn bana_5_1() -> Result<()> { #[test] fn bana_5_2() -> Result<()> { + // BANA 2026 Ex 5-7 let expr = "x2+y2=C"; test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠖⠽⠔⠼⠃⠀⠐⠶⠀⠰⠠⠉")?; return Ok(()); @@ -35,6 +39,7 @@ fn bana_5_2() -> Result<()> { #[test] fn bana_5_3() -> Result<()> { + // BANA 2026 Ex 5-10 let expr = "ab+cd"; test_braille("UEB", expr, "⠰⠰⠷⠁⠨⠌⠃⠾⠐⠖⠷⠉⠨⠌⠙⠾")?; return Ok(()); @@ -43,26 +48,27 @@ fn bana_5_3() -> Result<()> { #[test] fn bana_5_4() -> Result<()> { - let expr = "an×am= + // BANA 2026 Ex 5-14 (GTM 1.7: word indicator per sequence, not passage) + let expr = "an×am= an+m"; - test_braille("UEB", expr, "⠰⠰⠰⠁⠔⠝⠐⠦⠁⠔⠍⠀⠐⠶⠀⠁⠔⠣⠝⠐⠖⠍⠜⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠁⠔⠝⠐⠦⠁⠔⠍⠀⠐⠶⠀⠰⠰⠁⠔⠣⠝⠐⠖⠍⠜")?; return Ok(()); } #[test] fn bana_5_5() -> Result<()> { - let expr = "logxy"; - // BANA example contradicts GTM 9.2 that says don't use a space after a function name if there is an intervening indicator. - // Corrected: removed the space and the G1 indicator needed if a space were inserted - test_braille("UEB", expr, "⠰⠰⠇⠕⠛⠢⠭⠽")?; + // BANA 2026 Ex 5-8 (replaces 2019 log_x y example) + let expr = "log2x+logx"; + test_braille("UEB", expr, "⠇⠕⠛⠰⠢⠼⠃⠭⠐⠖⠇⠕⠛⠀⠰⠭")?; return Ok(()); } #[test] fn bana_5a_1() -> Result<()> { - let expr = "100°F"; + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice + let expr = "100°F"; test_braille("UEB", expr, "⠼⠁⠚⠚⠘⠚⠠⠋")?; return Ok(()); @@ -70,7 +76,17 @@ fn bana_5a_1() -> Result<()> { #[test] fn bana_5a_1_baseline() -> Result<()> { - let expr = "100°F"; + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice + let expr = "100°F"; + test_braille("UEB", expr, "⠼⠁⠚⠚⠘⠚⠠⠋")?; + return Ok(()); + +} + +#[test] +fn bana_5a_1_baseline_unit() -> Result<()> { + // Same as bana_5a_1_baseline; unit marked via data-intent-property + let expr = "100°F"; test_braille("UEB", expr, "⠼⠁⠚⠚⠘⠚⠠⠋")?; return Ok(()); @@ -78,22 +94,43 @@ fn bana_5a_1_baseline() -> Result<()> { #[test] fn bana_5a_2() -> Result<()> { + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice let expr = "25 km2"; test_braille("UEB", expr, "⠼⠃⠑⠀⠅⠍⠰⠔⠼⠃")?; return Ok(()); } +#[test] +fn bana_5a_2_unit() -> Result<()> { + // Same as bana_5a_2; unit marked via data-intent-property + let expr = "25 km2"; + test_braille("UEB", expr, "⠼⠃⠑⠀⠅⠍⠰⠔⠼⠃")?; + return Ok(()); + +} + #[test] fn bana_5a_2_mtext() -> Result<()> { + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice let expr = "25 km2"; test_braille("UEB", expr, "⠼⠃⠑⠀⠅⠍⠰⠔⠼⠃")?; return Ok(()); } +#[test] +fn bana_5a_2_mtext_unit() -> Result<()> { + // Same as bana_5a_2_mtext; unit marked via data-intent-property + let expr = "25 km2"; + test_braille("UEB", expr, "⠼⠃⠑⠀⠅⠍⠰⠔⠼⠃")?; + return Ok(()); + +} + #[test] fn bana_5a_3() -> Result<()> { + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice let expr = "6 m  s-1"; test_braille("UEB", expr, "⠼⠋⠀⠰⠍⠀⠰⠰⠎⠔⠣⠐⠤⠼⠁⠜")?; @@ -101,11 +138,183 @@ fn bana_5a_3() -> Result<()> { } +#[test] +fn bana_5a_3_unit() -> Result<()> { + // Same as bana_5a_3; unit marked via data-intent-property + let expr = "6 m  + s-1"; + test_braille("UEB", expr, "⠼⠋⠀⠰⠍⠀⠰⠰⠎⠔⠣⠐⠤⠼⠁⠜")?; + return Ok(()); + +} + #[test] fn bana_6_1() -> Result<()> { + // Punctuation before G1 terminator (BANA 2026 §5.4 / GTM 1.7.3b note) let expr = "x+y=z =t2."; - test_braille("UEB", expr, "⠰⠰⠰⠭⠐⠖⠽⠀⠐⠶⠀⠵⠀⠐⠶⠀⠞⠔⠼⠃⠲⠰⠄")?; + test_braille("UEB", expr, "⠭⠐⠖⠽⠀⠐⠶⠀⠰⠵⠀⠐⠶⠀⠞⠰⠔⠼⠃⠲")?; + return Ok(()); + +} + +// --- BANA 2026 examples not covered by older bana_* tests --- + +#[test] +fn bana2026_2_2() -> Result<()> { + // BANA 2026 Ex 2-2: spaced operation signs for beginning readers + let expr = "6-3=_"; + test_braille_prefs("UEB", vec![("UseSpacesAroundAllOperators", "true")], expr, "⠼⠋⠀⠐⠤⠀⠼⠉⠀⠐⠶⠀⠨⠤")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_2() -> Result<()> { + // BANA 2026 Ex 5-2: words with contractions in a technical expression + let expr = "Surface area=2lh+2lw+2wh"; + test_braille("UEB", expr, "⠠⠎⠥⠗⠋⠁⠉⠑⠀⠜⠑⠁⠀⠐⠶⠀⠼⠃⠇⠓⠐⠖⠼⠃⠇⠺⠐⠖⠼⠃⠺⠓")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_3() -> Result<()> { + // BANA 2026 Ex 5-3 + let expr = "NaCl+H2O"; + test_braille("UEB", expr, "⠠⠝⠁⠠⠉⠇⠐⠖⠠⠓⠰⠢⠼⠃⠠⠕")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_4() -> Result<()> { + // BANA 2026 Ex 5-4 + let expr = "5x2y3z"; + test_braille("UEB", expr, "⠰⠷⠼⠑⠭⠔⠼⠃⠨⠌⠽⠔⠼⠉⠵⠾")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_5() -> Result<()> { + // BANA 2026 Ex 5-5: 1×3 matrix and column (linearized like other matrix_* tests) + let expr = r#" + (abc) + ( + -1 + 2 + -3 + ) + "#; + test_braille("UEB", expr, "⠠⠐⠣⠁⠀⠰⠃⠀⠉⠠⠐⠜⠠⠐⠣⠐⠤⠼⠁⠠⠐⠜⠸⠀⠠⠐⠣⠼⠃⠠⠐⠜⠸⠀⠠⠐⠣⠐⠤⠼⠉⠠⠐⠜")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_6() -> Result<()> { + // BANA 2026 Ex 5-6 + let expr = r#" + (ab) + (-x2y) + "#; + test_braille("UEB", expr, "⠠⠐⠣⠁⠀⠃⠠⠐⠜⠠⠐⠣⠐⠤⠭⠠⠐⠜⠸⠀⠠⠐⠣⠼⠃⠽⠠⠐⠜")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_9_slope() -> Result<()> { + // BANA 2026 Ex 5-9 (math fragments): slope 3/5 and parallel lines l₁ ∥ l₂ + let expr = "l1l2"; + test_braille("UEB", expr, "⠇⠰⠢⠼⠁⠀⠼⠇⠀⠇⠰⠢⠼⠃")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_11() -> Result<()> { + // BANA 2026 Ex 5-11 + let expr = "ab"; + test_braille("UEB", expr, "⠰⠰⠷⠁⠨⠌⠃⠾")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_12() -> Result<()> { + // BANA 2026 Ex 5-12: vector AB with arrow + let expr = "AB"; + test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠁⠃⠜⠘⠱")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_13() -> Result<()> { + // BANA 2026 Ex 5-13 + let expr = "x-1=1x"; + test_braille("UEB", expr, "⠰⠰⠭⠔⠣⠐⠤⠼⠁⠜⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠭⠾")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_15() -> Result<()> { + // BANA 2026 Ex 5-15: all-caps print uses capital passage (⠠⠠⠠ … ⠠⠄) inside G1 passage + let expr = "P=VI=I2R=V2R"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠠⠠⠏⠀⠐⠶⠀⠧⠊⠀⠐⠶⠀⠊⠔⠼⠃⠗⠀⠐⠶⠀⠷⠧⠔⠼⠃⠨⠌⠗⠾⠠⠄⠰⠄")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_16() -> Result<()> { + // BANA 2026 Ex 5-16: CaCO₃ ⇄ CaO + CO₂ + let expr = "CaCO3 + + CaO+ + CO2"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠉⠁⠠⠉⠠⠕⠢⠼⠉⠀⠳⠕⠻⠳⠪⠀⠠⠉⠁⠠⠕⠐⠖⠠⠉⠠⠕⠢⠼⠃⠰⠄")?; + return Ok(()); + +} + +#[test] +fn bana2026_7_1() -> Result<()> { + // BANA 2026 Ex 7-1: V = lim Δx→0 Σ f(xᵢ)Δx (single-line) + let expr = r#"V= + limΔx0 + i + f(xi)Δx + "#; + test_braille("UEB", expr, "⠰⠠⠧⠀⠐⠶⠀⠰⠰⠇⠊⠍⠨⠢⠣⠠⠨⠙⠭⠳⠕⠼⠚⠜⠠⠨⠎⠨⠢⠊⠋⠐⠣⠭⠢⠊⠐⠜⠠⠨⠙⠭")?; + return Ok(()); + +} + +#[test] +fn bana2026_7_2() -> Result<()> { + // BANA 2026 Ex 7-2: first step of radical simplification chain + let expr = r#"x= + 36-4(9)+3(12)18 + "#; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠰⠷⠩⠼⠉⠋⠐⠤⠼⠙⠐⠣⠼⠊⠐⠜⠐⠖⠼⠉⠐⠣⠼⠁⠃⠐⠜⠬⠨⠌⠼⠁⠓⠾")?; + return Ok(()); + +} + +#[test] +fn bana2026_7_3() -> Result<()> { + // BANA 2026 Ex 7-3 + let expr = r#"x= + + 54+7-111×2+8000+1000 + -42 + + "#; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠷⠼⠑⠙⠐⠖⠼⠛⠐⠤⠼⠁⠁⠁⠐⠦⠼⠃⠐⠖⠼⠓⠚⠚⠚⠐⠖⠼⠁⠚⠚⠚⠨⠌⠐⠤⠼⠙⠃⠾")?; return Ok(()); } @@ -167,7 +376,7 @@ fn grade1_1_7_2() -> Result<()> { #[test] fn grade1_1_7_3_1() -> Result<()> { let expr = " - 3x4y+ + 3x4y+ y2 = x2 @@ -193,13 +402,614 @@ fn grade1_1_7_3_2() -> Result<()> { #[test] fn grade1_1_7_4() -> Result<()> { let expr = " - (yx2) + (yx2) "; test_braille("UEB", expr, "⠰⠰⠩⠐⠣⠽⠐⠤⠭⠔⠼⠃⠐⠜⠬")?; return Ok(()); } +// Additional examples from GTM 1.7 (July 2025): +// https://iceb.org/wp-content/uploads/2026/02/GTM-1.7-Grade-1-Indicators-Approved.pdf +// Ordinary surrounding text is omitted; braille is the preferred math from the spec. + +#[test] +fn grade1_1_7_3_a_1() -> Result<()> { + let expr = "x2"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_3_a_2() -> Result<()> { + let expr = "x"; + test_braille("UEB", expr, "⠰⠭⠀⠰⠳⠕⠀⠼⠿")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_3_a_3() -> Result<()> { + let expr = "xy"; + test_braille("UEB", expr, "⠰⠰⠷⠭⠨⠌⠽⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_3_b_1() -> Result<()> { + let expr = "xa=xb + =xc"; + test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠁⠀⠐⠶⠀⠭⠢⠃⠀⠐⠶⠀⠭⠢⠉⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_3_b_2() -> Result<()> { + let expr = "y=x2-4; +  y=x2-2x; +  y=x-x2."; + test_braille("UEB", expr, "⠰⠰⠰⠽⠀⠐⠶⠀⠭⠔⠼⠃⠐⠤⠼⠙⠆⠀⠽⠀⠐⠶⠀⠭⠔⠼⠃⠐⠤⠼⠃⠭⠆⠀⠽⠀⠐⠶⠀⠭⠐⠤⠭⠔⠼⠃⠲⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_4_1() -> Result<()> { + let expr = "x2-x-2 + =0"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠭⠐⠤⠼⠃⠀⠐⠶⠀⠼⠚")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_4_2() -> Result<()> { + let expr = "x2-4x-3 + =0"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠙⠭⠐⠤⠼⠉⠀⠐⠶⠀⠼⠚")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_4_3() -> Result<()> { + let expr = "x2-1=0"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠁⠀⠐⠶⠀⠼⠚")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_5_a() -> Result<()> { + let expr = "speed= + distancetime"; + // GTM 1.7.5(a) (2025): two grade 1 symbol indicators so the words stay contracted. + // After the fraction open, "dis" is not a word start (RUEB 10.6.2), so st+ance: ⠙⠊⠌⠨⠑. + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠷⠙⠊⠌⠨⠑⠨⠌⠐⠞⠰⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_5_b() -> Result<()> { + let expr = "luminositysun"; + test_braille("UEB", expr, "⠰⠰⠇⠥⠍⠊⠝⠕⠎⠊⠞⠽⠢⠣⠎⠥⠝⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_5_c() -> Result<()> { + let expr = "speed= + distancetime= + 30,000 m + 60 s= + 500 m/s"; + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾⠀⠐⠶⠀⠷⠼⠉⠚⠂⠚⠚⠚⠀⠍⠨⠌⠼⠋⠚⠀⠎⠾⠀⠐⠶⠀⠼⠑⠚⠚⠀⠍⠸⠌⠎⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_5_c_unit() -> Result<()> { + // Same as grade1_1_7_5_c; unit marked via data-intent-property + let expr = "speed= + distancetime= + 30,000 m + 60 s= + 500 m/s"; + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾⠀⠐⠶⠀⠷⠼⠉⠚⠂⠚⠚⠚⠀⠍⠨⠌⠼⠋⠚⠀⠎⠾⠀⠐⠶⠀⠼⠑⠚⠚⠀⠍⠸⠌⠎⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_1() -> Result<()> { + let expr = "y=x"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠭")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_2() -> Result<()> { + let expr = "25=5"; + test_braille("UEB", expr, "⠰⠩⠼⠃⠑⠬⠀⠐⠶⠀⠼⠑")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_3() -> Result<()> { + let expr = "x=7"; + test_braille("UEB", expr, "⠰⠰⠩⠭⠬⠀⠐⠶⠀⠼⠛")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_4() -> Result<()> { + let expr = "ms + -1"; + test_braille("UEB", expr, "⠰⠰⠍⠎⠔⠣⠐⠤⠼⠁⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_4_unit() -> Result<()> { + // Same as grade1_1_7_9_4; unit marked via data-intent-property + let expr = "ms + -1"; + test_braille("UEB", expr, "⠰⠰⠍⠎⠔⠣⠐⠤⠼⠁⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_5() -> Result<()> { + let expr = "y=x2"; + // GTM 1.7.9 (July 2025): one symbol indicator; numeric mode covers the closer. + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠷⠭⠨⠌⠼⠃⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_6() -> Result<()> { + let expr = "y=x22"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠰⠷⠭⠔⠼⠃⠨⠌⠼⠃⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_7() -> Result<()> { + let expr = "xy=cd"; + test_braille("UEB", expr, "⠰⠰⠷⠭⠨⠌⠽⠾⠀⠐⠶⠀⠰⠰⠷⠉⠨⠌⠙⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_8() -> Result<()> { + let expr = "(x,y)"; + test_braille("UEB", expr, "⠐⠣⠰⠭⠂⠀⠰⠽⠐⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_9() -> Result<()> { + let expr = "(xi,yi)"; + test_braille("UEB", expr, "⠐⠣⠭⠰⠢⠊⠂⠀⠽⠰⠢⠊⠐⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_10() -> Result<()> { + let expr = "(xi2, + yi2)"; + test_braille("UEB", expr, "⠰⠰⠐⠣⠭⠢⠊⠔⠼⠃⠂⠀⠰⠰⠽⠢⠊⠔⠼⠃⠐⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_11() -> Result<()> { + let expr = "B¯"; + test_braille("UEB", expr, "⠠⠃⠰⠱")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_12() -> Result<()> { + let expr = "M"; + test_braille("UEB", expr, "⠠⠍⠨⠔⠰⠳⠺⠗⠕")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_13() -> Result<()> { + let expr = "AZ + "; + test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠁⠵⠜⠨⠔⠳⠺⠗⠕")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_14() -> Result<()> { + let expr = "1327Al"; + test_braille("UEB", expr, "⠰⠢⠼⠁⠉⠔⠼⠃⠛⠠⠁⠇")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_15() -> Result<()> { + let expr = "O2-"; + test_braille("UEB", expr, "⠰⠰⠠⠕⠔⠣⠼⠃⠐⠤⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_16() -> Result<()> { + let expr = "2H2 + (g)+ + O2 + (g) + 2H2 + O(l)"; + test_braille("UEB", expr, "⠼⠃⠠⠓⠢⠼⠃⠐⠣⠛⠐⠜⠐⠖⠠⠕⠢⠼⠃⠐⠣⠛⠐⠜⠀⠰⠳⠕⠀⠼⠃⠠⠓⠢⠼⠃⠠⠕⠐⠣⠇⠐⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_17() -> Result<()> { + let expr = "y=mx+c"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠍⠭⠐⠖⠉")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_18() -> Result<()> { + let expr = "A=πr2"; + test_braille("UEB", expr, "⠠⠁⠀⠐⠶⠀⠨⠏⠗⠰⠔⠼⠃")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_19() -> Result<()> { + let expr = "E=mc2"; + test_braille("UEB", expr, "⠰⠠⠑⠀⠐⠶⠀⠍⠉⠰⠔⠼⠃")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_20() -> Result<()> { + let expr = "x2-2x+1 + =(x-1)2"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠃⠭⠐⠖⠼⠁⠀⠐⠶⠀⠐⠣⠭⠐⠤⠼⠁⠐⠜⠔⠼⠃")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_21() -> Result<()> { + let expr = "xn=1+ + -1nn"; + test_braille("UEB", expr, "⠭⠰⠢⠝⠀⠐⠶⠀⠼⠁⠐⠖⠷⠐⠤⠼⠁⠔⠝⠨⠌⠝⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_22() -> Result<()> { + let expr = "xn= + 2nn2"; + test_braille("UEB", expr, "⠭⠰⠢⠝⠀⠐⠶⠀⠰⠷⠼⠃⠔⠝⠨⠌⠝⠔⠼⠃⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_23() -> Result<()> { + let expr = "x= + + b± + b24ac + + 2a + + "; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠰⠷⠐⠤⠃⠸⠖⠩⠃⠔⠼⠃⠐⠤⠼⠙⠰⠁⠉⠬⠨⠌⠼⠃⠰⠁⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_24() -> Result<()> { + let expr = "xsinθ"; + test_braille("UEB", expr, "⠰⠰⠭⠩⠎⠊⠝⠨⠹⠬")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_25() -> Result<()> { + let expr = "Version1b"; + test_braille("UEB", expr, "⠰⠰⠠⠧⠑⠗⠎⠊⠕⠝⠔⠣⠼⠁⠰⠃⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_26() -> Result<()> { + let expr = "W=Fs"; + test_braille("UEB", expr, "⠰⠠⠺⠀⠐⠶⠀⠠⠋⠎")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_27() -> Result<()> { + let expr = "work=force×distance"; + test_braille("UEB", expr, "⠐⠺⠀⠐⠶⠀⠿⠉⠑⠐⠦⠙⠊⠌⠨⠑")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_28() -> Result<()> { + let expr = "force= + workdistance"; + test_braille("UEB", expr, "⠰⠠⠡⠀⠿⠉⠑⠀⠐⠶⠀⠰⠷⠐⠺⠨⠌⠙⠊⠌⠨⠑⠰⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_29() -> Result<()> { + let expr = "acceleration= + Δspeed + Δtime"; + test_braille("UEB", expr, "⠁⠒⠑⠇⠻⠁⠰⠝⠀⠐⠶⠀⠰⠷⠠⠨⠙⠎⠏⠑⠫⠨⠌⠠⠨⠙⠐⠞⠰⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_30() -> Result<()> { + let expr = "frequency=1time"; + test_braille("UEB", expr, "⠋⠗⠑⠟⠥⠢⠉⠽⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠞⠊⠍⠑⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_31() -> Result<()> { + let expr = "frequency=1/time"; + test_braille("UEB", expr, "⠋⠗⠑⠟⠥⠢⠉⠽⠀⠐⠶⠀⠼⠁⠸⠌⠞⠊⠍⠑")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_32() -> Result<()> { + let expr = "Vcone= + 13π + r2h"; + test_braille("UEB", expr, "⠰⠰⠠⠧⠢⠣⠉⠕⠝⠑⠜⠀⠐⠶⠀⠼⠁⠌⠉⠨⠏⠗⠔⠼⠃⠰⠓")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_33() -> Result<()> { + let expr = "V=π + y2dx"; + test_braille("UEB", expr, "⠰⠠⠧⠀⠐⠶⠀⠰⠰⠮⠨⠏⠽⠔⠼⠃⠰⠙⠭")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_34() -> Result<()> { + let expr = "period=2π + + lcosα + g + "; + test_braille("UEB", expr, "⠏⠻⠊⠕⠙⠀⠐⠶⠀⠼⠃⠨⠏⠩⠷⠇⠀⠰⠰⠉⠕⠎⠨⠁⠨⠌⠛⠾⠬")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_35() -> Result<()> { + let expr = "Assume g=9.81  + ms + -2"; + test_braille("UEB", expr, "⠠⠁⠎⠎⠥⠍⠑⠀⠰⠛⠀⠐⠶⠀⠼⠊⠲⠓⠁⠀⠰⠰⠍⠎⠔⠣⠐⠤⠼⠃⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_35_unit() -> Result<()> { + // Same as grade1_1_7_9_35; unit marked via data-intent-property + let expr = "Assume g=9.81  + ms + -2"; + test_braille("UEB", expr, "⠠⠁⠎⠎⠥⠍⠑⠀⠰⠛⠀⠐⠶⠀⠼⠊⠲⠓⠁⠀⠰⠰⠍⠎⠔⠣⠐⠤⠼⠃⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_36() -> Result<()> { + let expr = "v1=x  + ms + -1"; + test_braille("UEB", expr, "⠰⠰⠰⠧⠢⠼⠁⠀⠐⠶⠀⠭⠀⠍⠎⠔⠣⠐⠤⠼⠁⠜⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_36_unit() -> Result<()> { + // Same as grade1_1_7_9_36; unit marked via data-intent-property + let expr = "v1=x  + ms + -1"; + test_braille("UEB", expr, "⠰⠰⠰⠧⠢⠼⠁⠀⠐⠶⠀⠭⠀⠍⠎⠔⠣⠐⠤⠼⠁⠜⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_37() -> Result<()> { + let expr = "P= + t,u,v"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠏⠀⠐⠶⠀⠸⠣⠞⠂⠀⠥⠂⠀⠧⠸⠜⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_38() -> Result<()> { + let expr = "xa=xb + =xc=xd"; + test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠁⠀⠐⠶⠀⠭⠢⠃⠀⠐⠶⠀⠭⠢⠉⠀⠐⠶⠀⠭⠢⠙⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_39() -> Result<()> { + let expr = "x2×x3 + =x2+3 + =x5"; + test_braille("UEB", expr, "⠰⠰⠰⠭⠔⠼⠃⠐⠦⠭⠔⠼⠉⠀⠐⠶⠀⠭⠔⠣⠼⠃⠐⠖⠼⠉⠜⠀⠐⠶⠀⠭⠔⠼⠑⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_40() -> Result<()> { + let expr = "am×an + =am+n"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠍⠐⠦⠁⠔⠝⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠐⠖⠝⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_41() -> Result<()> { + let expr = "(am)n + =amn"; + test_braille("UEB", expr, "⠰⠰⠐⠣⠁⠔⠍⠐⠜⠔⠝⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠝⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_42() -> Result<()> { + let expr = "aman + =am-n"; + test_braille("UEB", expr, "⠰⠰⠷⠁⠔⠍⠨⠌⠁⠔⠝⠾⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠐⠤⠝⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_43() -> Result<()> { + let expr = "a-m + =1am"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠣⠐⠤⠍⠜⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠁⠔⠍⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_44() -> Result<()> { + let expr = "a0=1"; + test_braille("UEB", expr, "⠁⠰⠔⠼⠚⠀⠐⠶⠀⠼⠁")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_45() -> Result<()> { + let expr = "a1n + =an"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠷⠼⠁⠨⠌⠝⠾⠀⠐⠶⠀⠰⠰⠩⠔⠝⠁⠬")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_46() -> Result<()> { + let expr = "BC"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠃⠀⠳⠕⠀⠠⠉⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_47() -> Result<()> { + let expr = "x+y=5"; + test_braille("UEB", expr, "⠭⠐⠖⠽⠀⠐⠶⠀⠼⠑")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_48() -> Result<()> { + let expr = "CH4 + +4Cl2 + + CCl4 + +4HCl"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠉⠠⠓⠢⠼⠙⠐⠖⠼⠙⠠⠉⠇⠢⠼⠃⠀⠳⠕⠀⠠⠉⠠⠉⠇⠢⠼⠙⠐⠖⠼⠙⠠⠓⠠⠉⠇⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_49() -> Result<()> { + let expr = "614C + + 714N + + + -10β"; + test_braille("UEB", expr, "⠰⠰⠰⠢⠼⠋⠔⠼⠁⠙⠠⠉⠀⠳⠕⠀⠢⠼⠛⠔⠼⠁⠙⠠⠝⠐⠖⠢⠣⠐⠤⠼⠁⠜⠔⠼⠚⠨⠃⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_50() -> Result<()> { + let expr = "reliability= + number of faultstotal number of items + =p"; + test_braille("UEB", expr, "⠰⠰⠰⠗⠑⠇⠊⠁⠃⠊⠇⠊⠞⠽⠀⠐⠶⠀⠷⠝⠥⠍⠃⠑⠗⠀⠕⠋⠀⠋⠁⠥⠇⠞⠎⠨⠌⠞⠕⠞⠁⠇⠀⠝⠥⠍⠃⠑⠗⠀⠕⠋⠀⠊⠞⠑⠍⠎⠾⠀⠐⠶⠀⠏⠰⠄")?; + return Ok(()); + +} + #[test] fn number_2_1_2() -> Result<()> { let expr = "3,000"; @@ -313,7 +1123,7 @@ fn bold_2_7_3() -> Result<()> { fn signs_2_10_2() -> Result<()> { let expr = "$0.30,  30c  - or 30¢"; + or 30¢"; test_braille("UEB", expr, "⠈⠎⠼⠚⠲⠉⠚⠂⠀⠼⠉⠚⠰⠉⠀⠕⠗⠀⠼⠉⠚⠈⠉")?; return Ok(()); @@ -323,7 +1133,7 @@ fn signs_2_10_2() -> Result<()> { fn signs_2_10_5() -> Result<()> { let expr = "1 ft 6 in  or  - 1 6"; + 1 6"; test_braille("UEB", expr, "⠼⠁⠀⠋⠞⠀⠼⠋⠀⠔⠀⠕⠗⠀⠼⠁⠶⠀⠼⠋⠶⠶")?; return Ok(()); @@ -331,8 +1141,8 @@ fn signs_2_10_5() -> Result<()> { #[test] fn signs_2_10_8() -> Result<()> { - let expr = "0°C or -  32°F"; + let expr = "0°C or +  32°F"; test_braille("UEB", expr, "⠼⠚⠘⠚⠠⠉⠀⠕⠗⠀⠼⠉⠃⠘⠚⠠⠋")?; return Ok(()); @@ -340,9 +1150,9 @@ fn signs_2_10_8() -> Result<()> { #[test] fn signs_2_10_16() -> Result<()> { - let expr = "1 Å= + let expr = "1 Å= 110,000  - μ"; + μ"; test_braille("UEB", expr, "⠼⠁⠀⠠⠘⠫⠁⠀⠐⠶⠀⠼⠁⠌⠁⠚⠂⠚⠚⠚⠀⠨⠍")?; return Ok(()); @@ -384,7 +1194,7 @@ fn expr_3_1_2() -> Result<()> { #[test] fn expr_3_1_3() -> Result<()> { - let expr = "3×5=5×3=15"; + let expr = "3×5=5×3=15"; test_braille("UEB", expr, "⠼⠉⠐⠦⠼⠑⠀⠐⠶⠀⠼⠑⠐⠦⠼⠉⠀⠐⠶⠀⠼⠁⠑")?; return Ok(()); @@ -393,7 +1203,7 @@ fn expr_3_1_3() -> Result<()> { #[test] fn expr_3_1_6() -> Result<()> { // example includes spaces, so does the MathML (from WIRIS) - let expr = "5.72 m÷10= + let expr = "5.72 m÷10= 57.2 cm"; test_braille("UEB", expr, "⠼⠑⠲⠛⠃⠀⠍⠐⠌⠼⠁⠚⠀⠐⠶⠀⠼⠑⠛⠲⠃⠀⠉⠍")?; return Ok(()); @@ -402,7 +1212,7 @@ fn expr_3_1_6() -> Result<()> { #[test] fn expr_3_1_7() -> Result<()> { - let expr = "15±0.5"; + let expr = "15±0.5"; test_braille("UEB", expr, "⠼⠁⠑⠸⠖⠼⠚⠲⠑")?; return Ok(()); @@ -411,7 +1221,7 @@ fn expr_3_1_7() -> Result<()> { #[test] fn expr_3_1_8() -> Result<()> { let expr = "Area=bh= - 5·3=15"; + 5·3=15"; test_braille("UEB", expr, "⠠⠜⠑⠁⠀⠐⠶⠀⠃⠓⠀⠐⠶⠀⠼⠑⠐⠲⠼⠉⠀⠐⠶⠀⠼⠁⠑")?; return Ok(()); @@ -419,7 +1229,7 @@ fn expr_3_1_8() -> Result<()> { #[test] fn expr_3_1_9_wiris() -> Result<()> { - let expr = "3.9×4.116"; + let expr = "3.9×4.116"; test_braille("UEB", expr, "⠼⠉⠲⠊⠐⠦⠼⠙⠲⠁⠀⠸⠔⠀⠼⠁⠋")?; return Ok(()); @@ -427,7 +1237,7 @@ fn expr_3_1_9_wiris() -> Result<()> { #[test] fn expr_3_1_9() -> Result<()> { - let expr = "3.9×4.116"; + let expr = "3.9×4.116"; test_braille("UEB", expr, "⠼⠉⠲⠊⠐⠦⠼⠙⠲⠁⠀⠸⠔⠀⠼⠁⠋")?; return Ok(()); @@ -435,7 +1245,7 @@ fn expr_3_1_9() -> Result<()> { #[test] fn expr_3_1_10() -> Result<()> { - let expr = "5-33-5"; + let expr = "5-33-5"; test_braille("UEB", expr, "⠼⠑⠐⠤⠼⠉⠀⠐⠶⠈⠱⠀⠼⠉⠐⠤⠼⠑")?; return Ok(()); @@ -459,7 +1269,7 @@ fn ratio_3_1_12() -> Result<()> { #[test] fn alg_3_2_1_1() -> Result<()> { - let expr = "yx"; + let expr = "yx"; test_braille("UEB", expr, "⠰⠽⠀⠸⠐⠶⠀⠰⠭")?; return Ok(()); @@ -475,8 +1285,8 @@ fn alg_3_2_1_2() -> Result<()> { #[test] fn alg_3_2_2() -> Result<()> { - let expr = "0θ - 2π"; + let expr = "0θ + 2π"; test_braille("UEB", expr, "⠼⠚⠀⠸⠈⠣⠀⠨⠹⠀⠸⠈⠣⠀⠼⠃⠨⠏")?; return Ok(()); @@ -502,10 +1312,7 @@ fn alg_3_2_4() -> Result<()> { #[test] fn alg_3_2_5() -> Result<()> { let expr = "d+ab=ac"; - // BANA says use a word indicator if G1 not in first 3 cells (it is after the '='); use passage if >=2 whitespace - // This seems like a poor choice in this case since there is only one G1 indicator, but that's the BANA guidance so... - // "⠰⠰⠰⠙⠐⠖⠁⠃⠀⠐⠶⠀⠁⠉⠰⠄" - // GTM says to use the following and it is more sensisble, so I'm going with it + // GTM 1.7 / BANA 2026: one G1 symbol for standing-alone 'a' after '=' test_braille("UEB", expr, "⠙⠐⠖⠁⠃⠀⠐⠶⠀⠰⠁⠉")?; return Ok(()); @@ -541,10 +1348,10 @@ fn example_3_4_1() -> Result<()> { fn example_3_4_2() -> Result<()> { // removed some cruft from TeX output of {}^{-}2+{}^{+}3, but the basics are preserved let expr = " - + 2 + - + 3 "; test_braille("UEB", expr, "⠰⠔⠐⠤⠼⠃⠐⠖⠔⠐⠤⠼⠉")?; @@ -578,7 +1385,7 @@ fn omission_3_6_3() -> Result<()> { #[test] fn omission_3_6_4() -> Result<()> { - let expr = "37=10"; + let expr = "37=10"; test_braille("UEB", expr, "⠼⠉⠫⠼⠙⠱⠼⠛⠀⠐⠶⠀⠼⠁⠚")?; return Ok(()); @@ -637,9 +1444,21 @@ fn fraction_6_2_1() -> Result<()> { #[test] fn fraction_6_2_2() -> Result<()> { let expr = "1750 -  cm= +  cm= + 134 +  m"; + test_braille("UEB", expr, "⠼⠁⠛⠑⠚⠀⠉⠍⠀⠐⠶⠀⠼⠁⠼⠉⠌⠙⠀⠰⠍")?; + return Ok(()); + +} + +#[test] +fn fraction_6_2_2_unit() -> Result<()> { + // Same as fraction_6_2_2; unit marked via data-intent-property + let expr = "1750 +  cm= 134 -  m"; +  m"; test_braille("UEB", expr, "⠼⠁⠛⠑⠚⠀⠉⠍⠀⠐⠶⠀⠼⠁⠼⠉⠌⠙⠀⠰⠍")?; return Ok(()); @@ -648,26 +1467,30 @@ fn fraction_6_2_2() -> Result<()> { #[test] fn fraction_6_2_2_unicode_frac() -> Result<()> { let expr = "1750 -  cm= - 1¾ -  m"; +  cm= + 1¾ +  m"; test_braille("UEB", expr, "⠼⠁⠛⠑⠚⠀⠉⠍⠀⠐⠶⠀⠼⠁⠼⠉⠌⠙⠀⠰⠍")?; return Ok(()); } #[test] -fn fraction_6_3_1() -> Result<()> { - let expr = "3/8"; - test_braille("UEB", expr, "⠼⠉⠸⠌⠼⠓")?; +fn fraction_6_2_2_unicode_frac_unit() -> Result<()> { + // Same as fraction_6_2_2_unicode_frac; unit marked via data-intent-property + let expr = "1750 +  cm= + 1¾ +  m"; + test_braille("UEB", expr, "⠼⠁⠛⠑⠚⠀⠉⠍⠀⠐⠶⠀⠼⠁⠼⠉⠌⠙⠀⠰⠍")?; return Ok(()); } #[test] -fn fraction_6_4_1() -> Result<()> { - let expr = "y=x2"; - test_braille("UEB", expr, "⠰⠰⠰⠽⠀⠐⠶⠀⠷⠭⠨⠌⠼⠃⠾⠰⠄")?; +fn fraction_6_3_1() -> Result<()> { + let expr = "3/8"; + test_braille("UEB", expr, "⠼⠉⠸⠌⠼⠓")?; return Ok(()); } @@ -716,8 +1539,9 @@ fn fraction_6_4_5() -> Result<()> { #[test] fn fraction_6_4_6() -> Result<()> { let expr = "speed=distancetime"; - // GTM lists two options: "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾" and "⠰⠰⠰⠎⠏⠑⠑⠙⠀⠐⠶⠀⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾⠰⠄" - test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾")?; + // GTM 2014 §6.4 listed word-indicator and passage alternatives (uncontracted words). + // GTM 1.7.5(a) (2025) prefers two grade 1 symbol indicators with contracted words. + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠷⠙⠊⠌⠨⠑⠨⠌⠐⠞⠰⠾")?; return Ok(()); } @@ -766,7 +1590,7 @@ fn msup_7_3_7() -> Result<()> { #[test] fn msup_7_3_11() -> Result<()> { let expr = "xaby=x"; - test_braille("UEB", expr, "⠰⠰⠰⠭⠔⠷⠁⠨⠌⠃⠾⠽⠀⠐⠶⠀⠭⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠭⠔⠷⠁⠨⠌⠃⠾⠽⠀⠐⠶⠀⠰⠭")?; return Ok(()); } @@ -798,7 +1622,7 @@ fn msub_7_4_3() -> Result<()> { #[test] fn msup_7_5_1() -> Result<()> { let expr = "0.0045= - 4.5×10-3 + 4.5×10-3 "; test_braille("UEB", expr, "⠼⠚⠲⠚⠚⠙⠑⠀⠐⠶⠀⠼⠙⠲⠑⠐⠦⠼⠁⠚⠔⠣⠐⠤⠼⠉⠜")?; return Ok(()); @@ -824,7 +1648,7 @@ fn msup_7_6_2() -> Result<()> { #[test] fn msubsup_7_7_1() -> Result<()> { let expr = "x12=y23"; - test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠼⠁⠔⠼⠃⠀⠐⠶⠀⠽⠢⠼⠃⠔⠼⠉⠰⠄")?; + test_braille("UEB", expr, "⠭⠰⠢⠼⠁⠔⠼⠃⠀⠐⠶⠀⠽⠰⠢⠼⠃⠔⠼⠉")?; return Ok(()); } @@ -852,7 +1676,7 @@ fn pre_sup_7_8_2() -> Result<()> { +3- =5- "; - test_braille("UEB", expr, "⠰⠰⠰⠔⠐⠤⠼⠃⠐⠖⠔⠐⠤⠼⠉⠀⠐⠶⠀⠔⠐⠤⠼⠑⠰⠄")?; + test_braille("UEB", expr, "⠰⠔⠐⠤⠼⠃⠐⠖⠔⠐⠤⠼⠉⠀⠐⠶⠀⠰⠔⠐⠤⠼⠑")?; return Ok(()); } @@ -860,9 +1684,9 @@ fn pre_sup_7_8_2() -> Result<()> { #[test] fn sum_7_9_1() -> Result<()> { - let expr = "x=1n + let expr = "x=1n xi2"; - test_braille("UEB", expr, "⠰⠰⠠⠨⠎⠨⠢⠣⠭⠐⠶⠼⠁⠜⠨⠔⠝⠭⠢⠊⠔⠼⠃")?; + test_braille("UEB", expr, "⠠⠨⠎⠨⠢⠰⠣⠭⠐⠶⠼⠁⠜⠨⠔⠝⠭⠢⠊⠔⠼⠃")?; return Ok(()); } @@ -870,7 +1694,7 @@ fn sum_7_9_1() -> Result<()> { #[test] fn lim_7_9_2() -> Result<()> { // Note: modified because passage indicator is not needed (same expr when word indicator is used) - let expr = "limxa + let expr = "limxa f(x)=1"; test_braille("UEB", expr, "⠰⠰⠇⠊⠍⠨⠢⠣⠭⠳⠕⠁⠜⠋⠐⠣⠭⠐⠜⠀⠐⠶⠀⠼⠁")?; return Ok(()); @@ -889,7 +1713,7 @@ fn sqrt_8_1_1() -> Result<()> { fn sqrt_8_1_2() -> Result<()> { let expr = "r= x2+y2"; - test_braille("UEB", expr, "⠰⠰⠰⠗⠀⠐⠶⠀⠩⠭⠔⠼⠃⠐⠖⠽⠔⠼⠃⠬⠰⠄")?; + test_braille("UEB", expr, "⠰⠗⠀⠐⠶⠀⠰⠰⠩⠭⠔⠼⠃⠐⠖⠽⠔⠼⠃⠬")?; return Ok(()); } @@ -899,7 +1723,7 @@ fn sqrt_8_1_3() -> Result<()> { let expr = " - 783.2×6.547 + 783.2×6.547 0.4628 @@ -915,17 +1739,17 @@ fn sqrt_8_1_4() -> Result<()> { x = - b± + b± b 2 - 4ac + 4ac 2a "; - test_braille("UEB", expr, "⠰⠰⠰⠭⠀⠐⠶⠀⠷⠐⠤⠃⠸⠖⠩⠃⠔⠼⠃⠐⠤⠼⠙⠰⠁⠉⠬⠨⠌⠼⠃⠰⠁⠾⠰⠄")?; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠰⠷⠐⠤⠃⠸⠖⠩⠃⠔⠼⠃⠐⠤⠼⠙⠰⠁⠉⠬⠨⠌⠼⠃⠰⠁⠾")?; return Ok(()); } @@ -946,7 +1770,7 @@ fn root_8_2_2() -> Result<()> { y3+ z3 3"; - test_braille("UEB", expr, "⠰⠰⠰⠟⠀⠐⠶⠀⠩⠔⠼⠉⠭⠔⠼⠉⠐⠖⠽⠔⠼⠉⠐⠖⠵⠔⠼⠉⠬⠰⠄")?; + test_braille("UEB", expr, "⠰⠟⠀⠐⠶⠀⠰⠰⠩⠔⠼⠉⠭⠔⠼⠉⠐⠖⠽⠔⠼⠉⠐⠖⠵⠔⠼⠉⠬")?; return Ok(()); } @@ -992,7 +1816,7 @@ fn spacing_9_3_1_1() -> Result<()> { #[test] fn spacing_9_3_1_2() -> Result<()> { - let expr = "3tan45°"; + let expr = "3tan45°"; test_braille("UEB", expr, "⠼⠉⠞⠁⠝⠼⠙⠑⠘⠚")?; return Ok(()); @@ -1016,7 +1840,7 @@ fn spacing_9_3_2_1() -> Result<()> { #[test] fn spacing_9_3_2_2() -> Result<()> { - let expr = "sinθ"; + let expr = "sinθ"; test_braille("UEB", expr, "⠎⠔⠨⠹")?; return Ok(()); @@ -1041,7 +1865,9 @@ fn spacing_9_3_2_4() -> Result<()> { #[test] fn spacing_9_3_2_5() -> Result<()> { let expr = "Limx2"; - test_braille("UEB", expr, "⠰⠰⠠⠇⠊⠍⠷⠭⠨⠌⠼⠃⠾")?; + // GTM 2014 9.3.2 used a word indicator (;;,lim(x./#b)). GTM 1.7.3(a): one + // symbol indicator on the fraction open; numeric mode covers the closer. + test_braille("UEB", expr, "⠠⠇⠊⠍⠰⠷⠭⠨⠌⠼⠃⠾")?; return Ok(()); } @@ -1091,8 +1917,8 @@ fn spacing_9_3_3_5() -> Result<()> { #[test] fn spacing_9_3_3_6() -> Result<()> { - let expr = "sin2β= - 2sinβcosβ"; + let expr = "sin2β= + 2sinβcosβ"; test_braille("UEB", expr, "⠎⠔⠼⠃⠨⠃⠀⠐⠶⠀⠼⠃⠎⠊⠝⠨⠃⠉⠕⠎⠨⠃")?; return Ok(()); @@ -1121,9 +1947,9 @@ fn text_9_7_1() -> Result<()> { fn stat_9_7_2() -> Result<()> { let expr = "Exp(R)= n2+1"; - // GTM uses "⠰⠰⠰⠠⠑⠭⠏⠐⠣⠠⠗⠐⠜⠀⠐⠶⠀⠷⠝⠨⠌⠼⠃⠾⠐⠖⠼⠁⠰⠄", - // but "⠠⠑⠭⠏⠐⠣⠠⠗⠐⠜⠀⠐⠶⠀⠰⠰⠷⠝⠨⠌⠼⠃⠾⠐⠖⠼⠁" is shorter and is consistent with omission_3_6_7 and fraction_6_4_6 - test_braille("UEB", expr, "⠠⠑⠭⠏⠐⠣⠠⠗⠐⠜⠀⠐⠶⠀⠰⠰⠷⠝⠨⠌⠼⠃⠾⠐⠖⠼⠁")?; + // GTM 2014 9.7 used a passage around the whole equation. GTM 1.7.3(a): one + // symbol indicator on n/2 (same as grade1_1_7_9_5). + test_braille("UEB", expr, "⠠⠑⠭⠏⠐⠣⠠⠗⠐⠜⠀⠐⠶⠀⠰⠷⠝⠨⠌⠼⠃⠾⠐⠖⠼⠁")?; return Ok(()); } @@ -1141,7 +1967,7 @@ fn set_10_1() -> Result<()> { #[test] fn set_10_3() -> Result<()> { - let expr = "3AB"; + let expr = "3AB"; test_braille("UEB", expr, "⠼⠉⠀⠘⠑⠀⠠⠁⠨⠦⠠⠃")?; return Ok(()); @@ -1149,7 +1975,7 @@ fn set_10_3() -> Result<()> { #[test] fn set_10_4() -> Result<()> { - let expr = "ABAB"; + let expr = "ABAB"; test_braille("UEB", expr, "⠠⠁⠨⠦⠠⠃⠀⠘⠣⠀⠠⠁⠨⠖⠠⠃")?; return Ok(()); @@ -1158,7 +1984,7 @@ fn set_10_4() -> Result<()> { fn set_10_5() -> Result<()> { let expr = "A'B'= (AB)'"; - test_braille("UEB", expr, "⠰⠰⠰⠠⠁⠶⠨⠖⠠⠃⠶⠀⠐⠶⠀⠐⠣⠠⠁⠨⠦⠠⠃⠐⠜⠶⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠠⠁⠶⠨⠖⠠⠃⠶⠀⠐⠶⠀⠐⠣⠠⠁⠨⠦⠠⠃⠐⠜⠰⠶")?; return Ok(()); } @@ -1166,11 +1992,9 @@ fn set_10_5() -> Result<()> { #[test] fn set_10_6() -> Result<()> { // Note: example uses the wrong char "├" in the display -- should be "⊢" - let expr = "[(pq)¬p] + let expr = "[(pq)¬p] q"; - // Acceptable: GTM does uses a G1 passage indicator: "⠰⠰⠰⠨⠣⠐⠣⠏⠈⠖⠟⠐⠜⠈⠦⠈⠹⠏⠨⠜⠀⠸⠒⠀⠟⠰⠄" - // However, the BANA G1 standing alone rule ("...before a single letter standing alone") applies, so start in G2 mode. - // Corrected to remove the passage indicator + // GTM / BANA 2026: grade 1 passage test_braille("UEB", expr, "⠨⠣⠐⠣⠏⠈⠖⠟⠐⠜⠈⠦⠈⠹⠏⠨⠜⠀⠸⠒⠀⠰⠟")?; return Ok(()); @@ -1187,9 +2011,7 @@ fn example_11_5_1_2() -> Result<()> { #[test] fn example_11_5_1_3() -> Result<()> { let expr = "f'(x)"; - // Acceptable: GTM uses a G1 start indicator: "⠰⠰⠋⠶⠐⠣⠭⠐⠜" - // However, BANA says don't use a word indicator if G1 is in first 3 cells (the ':' needs it) - // Corrected to avoid word indicator + // GTM / BANA 2026: grade 1 word indicator test_braille("UEB", expr, "⠋⠰⠶⠐⠣⠭⠐⠜")?; return Ok(()); @@ -1197,7 +2019,7 @@ fn example_11_5_1_3() -> Result<()> { #[test] fn example_11_5_1_4() -> Result<()> { - let expr = "yx"; + let expr = "yx"; test_braille("UEB", expr, "⠰⠰⠷⠈⠙⠽⠨⠌⠈⠙⠭⠾")?; return Ok(()); @@ -1205,11 +2027,11 @@ fn example_11_5_1_4() -> Result<()> { #[test] fn example_11_5_2() -> Result<()> { - let expr = "23(2x+1)dx + let expr = "23(2x+1)dx =x2+x23 =(32+3)-(22+2) =12-6=6"; - test_braille("UEB", expr, "⠰⠰⠰⠮⠢⠼⠃⠔⠼⠉⠐⠣⠼⠃⠭⠐⠖⠼⠁⠐⠜⠙⠭⠀⠐⠶⠀⠨⠣⠭⠔⠼⠃⠐⠖⠭⠨⠜⠢⠼⠃⠔⠼⠉⠀⠐⠶⠀⠐⠣⠼⠉⠔⠼⠃⠐⠖⠼⠉⠐⠜⠐⠤⠐⠣⠼⠃⠔⠼⠃⠐⠖⠼⠃⠐⠜⠀⠐⠶⠀⠼⠁⠃⠐⠤⠼⠋⠀⠐⠶⠀⠼⠋⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠮⠢⠼⠃⠔⠼⠉⠐⠣⠼⠃⠭⠐⠖⠼⠁⠐⠜⠙⠭⠀⠐⠶⠀⠨⠣⠭⠰⠔⠼⠃⠐⠖⠭⠨⠜⠢⠼⠃⠔⠼⠉⠀⠐⠶⠀⠐⠣⠼⠉⠔⠼⠃⠐⠖⠼⠉⠐⠜⠐⠤⠐⠣⠼⠃⠔⠼⠃⠐⠖⠼⠃⠐⠜⠀⠐⠶⠀⠼⠁⠃⠐⠤⠼⠋⠀⠐⠶⠀⠼⠋")?; return Ok(()); } @@ -1229,19 +2051,19 @@ fn example_11_5_3() -> Result<()> { = n! - r!(nr)! + r!(nr)! "; // modified to use "shape" as recommended in a comment on this example - test_braille("UEB", expr, "⠰⠰⠰⠔⠝⠠⠉⠢⠗⠀⠐⠶⠀⠐⠣⠝⠰⠻⠗⠐⠜⠀⠐⠶⠀⠷⠝⠖⠨⠌⠗⠖⠐⠣⠝⠐⠤⠗⠐⠜⠖⠾⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠔⠝⠠⠉⠢⠗⠀⠐⠶⠀⠐⠣⠝⠰⠻⠗⠐⠜⠀⠐⠶⠀⠰⠰⠷⠝⠖⠨⠌⠗⠖⠐⠣⠝⠐⠤⠗⠐⠜⠖⠾")?; return Ok(()); } #[test] fn example_11_5_4() -> Result<()> { - let expr = "a(bc) - =(ab)(ac)"; + let expr = "a(bc) + =(ab)(ac)"; test_braille("UEB", expr, "⠁⠐⠔⠐⠣⠃⠐⠴⠉⠐⠜⠀⠐⠶⠀⠐⠣⠁⠐⠔⠃⠐⠜⠐⠴⠐⠣⠁⠐⠔⠉⠐⠜")?; return Ok(()); @@ -1252,11 +2074,11 @@ fn example_11_5_5_2() -> Result<()> { let expr = " f - 1 + 1 : Y - + X "; test_braille("UEB", expr, "⠰⠰⠰⠋⠔⠣⠐⠤⠼⠁⠜⠒⠀⠠⠽⠀⠳⠕⠀⠠⠭⠰⠄")?; @@ -1268,17 +2090,17 @@ fn example_11_5_5_2() -> Result<()> { fn example_11_5_5_3() -> Result<()> { // this comes from MathJax let expr = " - + y - + Y - + x - + X "; - test_braille("UEB", expr, "⠰⠰⠰⠘⠁⠽⠀⠘⠑⠀⠠⠽⠀⠘⠢⠭⠀⠘⠑⠀⠠⠭⠰⠄")?; + test_braille("UEB", expr, "⠘⠁⠽⠀⠘⠑⠀⠰⠠⠽⠀⠘⠢⠭⠀⠘⠑⠀⠰⠠⠭")?; return Ok(()); } @@ -1291,7 +2113,7 @@ fn example_11_5_6() -> Result<()> { x + y = 6 return Ok(()); } "; - test_braille("UEB", expr, "⠰⠰⠰⠸⠣⠐⠣⠭⠂⠀⠽⠐⠜⠀⠸⠳⠀⠭⠐⠖⠽⠀⠐⠶⠀⠼⠋⠸⠜⠰⠄")?; + test_braille("UEB", expr, "⠸⠣⠐⠣⠰⠭⠂⠀⠰⠽⠐⠜⠀⠸⠳⠀⠭⠐⠖⠽⠀⠐⠶⠀⠼⠋⠸⠜")?; return Ok(()); } @@ -1315,7 +2137,7 @@ fn example_11_6() -> Result<()> { fn bar_over_12_1_1() -> Result<()> { let expr = "x_= 10+11+123"; - test_braille("UEB", expr, "⠰⠰⠰⠭⠱⠀⠐⠶⠀⠷⠼⠁⠚⠐⠖⠼⠁⠁⠐⠖⠼⠁⠃⠨⠌⠼⠉⠾⠰⠄")?; + test_braille("UEB", expr, "⠭⠰⠱⠀⠐⠶⠀⠰⠷⠼⠁⠚⠐⠖⠼⠁⠁⠐⠖⠼⠁⠃⠨⠌⠼⠉⠾")?; return Ok(()); } @@ -1346,8 +2168,8 @@ fn dot_12_1_4() -> Result<()> { #[test] fn dot_12_1_5() -> Result<()> { - let expr = "0.561˙ - 23˙"; + let expr = "0.561˙ + 23˙"; test_braille("UEB", expr, "⠼⠚⠲⠑⠋⠣⠼⠁⠜⠘⠲⠼⠃⠣⠼⠉⠜⠘⠲")?; return Ok(()); @@ -1355,7 +2177,7 @@ fn dot_12_1_5() -> Result<()> { #[test] fn dot_12_1_6_single() -> Result<()> { - let expr = "x˙"; + let expr = "x˙"; test_braille("UEB", expr, "⠭⠘⠲")?; return Ok(()); @@ -1363,7 +2185,7 @@ fn dot_12_1_6_single() -> Result<()> { #[test] fn dot_12_1_6_double() -> Result<()> { - let expr = "x¨"; + let expr = "x¨"; test_braille("UEB", expr, "⠰⠰⠭⠨⠔⠣⠲⠲⠜")?; return Ok(()); @@ -1372,9 +2194,7 @@ fn dot_12_1_6_double() -> Result<()> { #[test] fn hat_12_1_7() -> Result<()> { let expr = "AB^C"; - // Acceptable: GTM uses a G1 start indicator: "⠰⠰⠠⠁⠠⠃⠐⠱⠠⠉" - // BANA says use a word indicator if G1 not in first 3 cells (modified it to not count cap indicators since that helps with GTM compatibility) - // Corrected to skip the G1 indicator at the start (it's debatable as to which is better) + // GTM / BANA 2026: grade 1 word indicator test_braille("UEB", expr, "⠠⠁⠠⠃⠰⠐⠱⠠⠉")?; return Ok(()); @@ -1400,7 +2220,7 @@ fn arrow_under_12() -> Result<()> { #[test] fn bar_12_2_1() -> Result<()> { - let expr = "xy¯"; + let expr = "xy¯"; test_braille("UEB", expr, "⠰⠰⠭⠔⠣⠽⠱⠜")?; return Ok(()); @@ -1408,7 +2228,7 @@ fn bar_12_2_1() -> Result<()> { #[test] fn bar_12_2_2() -> Result<()> { - let expr = "xy¯"; + let expr = "xy¯"; test_braille("UEB", expr, "⠰⠰⠣⠭⠔⠽⠜⠱")?; return Ok(()); @@ -1416,7 +2236,7 @@ fn bar_12_2_2() -> Result<()> { #[test] fn shape_14_1_1_1() -> Result<()> { - let expr = " ABC"; + let expr = " ABC"; test_braille("UEB", expr, "⠰⠫⠼⠉⠀⠠⠠⠁⠃⠉")?; return Ok(()); @@ -1424,7 +2244,7 @@ fn shape_14_1_1_1() -> Result<()> { #[test] fn shape_14_1_2_1() -> Result<()> { - let expr = "ABC"; + let expr = "ABC"; test_braille("UEB", expr, "⠰⠫⠼⠉⠱⠠⠠⠁⠃⠉")?; return Ok(()); @@ -1433,11 +2253,11 @@ fn shape_14_1_2_1() -> Result<()> { #[test] fn shape_14_1_2_2() -> Result<()> { // the for the shapes are wrong -- but it isn't clear what they should be (from WIRIS editor) - let expr = "{, -  , -  , + let expr = "{, +  , +  , return Ok(()); -   }"; +   }"; test_braille("UEB", expr, "⠸⠣⠰⠫⠼⠙⠱⠂⠀⠨⠫⠿⠱⠂⠀⠸⠫⠼⠉⠱⠂⠀⠨⠫⠼⠙⠀⠲⠲⠲⠸⠜")?; return Ok(()); } @@ -1584,11 +2404,8 @@ fn omission_15_4_1() -> Result<()> { #[test] fn chem_16_2_8() -> Result<()> { let expr = "Ca(OH)2"; - // Acceptable: GTM does not use a G1 start indicator: "⠠⠉⠁⠐⠣⠠⠕⠠⠓⠐⠜⠰⠢⠼⠃" - // However, BANA says use a word indicator if G1 not in first 3 cells (it is before the subscript near the end); use passage if >=2 whitespace - // This seems like a debateable choice in this case since there is only one G1 indicator, but that's the BANA guidance so... - // Corrected to use word indicator - test_braille("UEB", expr, "⠰⠰⠠⠉⠁⠐⠣⠠⠕⠠⠓⠐⠜⠢⠼⠃")?; + // GTM / BANA 2026: single G1 symbol for subscript (no word indicator) + test_braille("UEB", expr, "⠠⠉⠁⠐⠣⠠⠕⠠⠓⠐⠜⠰⠢⠼⠃")?; return Ok(()); } @@ -1623,7 +2440,7 @@ fn chem_16_2_9() -> Result<()> { - + @@ -1658,11 +2475,8 @@ fn chem_16_2_9() -> Result<()> { "; - // Acceptable: GTM does not use a G1 start indicator: "⠠⠉⠥⠠⠎⠠⠕⠰⠢⠼⠙⠐⠲⠼⠑⠠⠓⠢⠼⠃⠠⠕" - // However, BANA says use a word indicator if G1 not in first 3 cells (it is before the subscript); use passage if >=2 whitespace - // This seems like a debatable choice in this case since there is only one G1 indicator, but that's the BANA guidance so... - // Corrected to use word indicator - test_braille("UEB", expr, "⠰⠰⠠⠉⠥⠠⠎⠠⠕⠢⠼⠙⠐⠲⠼⠑⠠⠓⠢⠼⠃⠠⠕")?; + // GTM / BANA 2026: single G1 symbol for first subscript (no word indicator) + test_braille("UEB", expr, "⠠⠉⠥⠠⠎⠠⠕⠰⠢⠼⠙⠐⠲⠼⠑⠠⠓⠢⠼⠃⠠⠕")?; return Ok(()); } @@ -1693,14 +2507,14 @@ fn chem_16_2_12() -> Result<()> { R - + CH ( OH ) - + CH @@ -1708,7 +2522,7 @@ fn chem_16_2_12() -> Result<()> { 2 - + CH @@ -1716,7 +2530,7 @@ fn chem_16_2_12() -> Result<()> { 2 - + CO @@ -1726,9 +2540,8 @@ fn chem_16_2_12() -> Result<()> { H "; - // GTM uses G2 mode and has two G1 indicators (in middle and near end). That definitely violates BANA guidelines and maybe there guidelines. - // I have switched it to G1 word mode, which seems better (same length, but no switching) - test_braille("UEB", expr, "⠰⠰⠠⠠⠠⠗⠐⠲⠉⠓⠐⠣⠕⠓⠐⠜⠐⠲⠉⠓⠢⠼⠃⠐⠲⠉⠓⠢⠼⠃⠐⠲⠉⠕⠢⠼⠃⠰⠓⠠⠄")?; + // GTM / BANA 2026: G2 with inline G1 symbols (not forced word mode) + test_braille("UEB", expr, "⠠⠠⠠⠗⠐⠲⠡⠐⠣⠕⠓⠐⠜⠐⠲⠡⠰⠢⠼⠃⠐⠲⠉⠓⠢⠼⠃⠐⠲⠉⠕⠢⠼⠃⠰⠓⠠⠄")?; return Ok(()); } @@ -1815,7 +2628,7 @@ fn chem_16_5_1() -> Result<()> { - + Na @@ -1861,7 +2674,7 @@ fn chem_16_5_1() -> Result<()> { "#; - test_braille("UEB", expr, "⠰⠰⠰⠼⠃⠠⠝⠁⠠⠕⠠⠓⠐⠖⠠⠓⠢⠼⠃⠠⠎⠠⠕⠢⠼⠙⠀⠳⠕⠀⠠⠝⠁⠢⠼⠃⠠⠎⠠⠕⠢⠼⠙⠐⠖⠼⠃⠠⠓⠢⠼⠃⠠⠕⠰⠄")?; + test_braille("UEB", expr, "⠼⠃⠠⠝⠁⠠⠕⠠⠓⠐⠖⠠⠓⠢⠼⠃⠠⠎⠠⠕⠢⠼⠙⠀⠰⠳⠕⠀⠠⠝⠁⠰⠢⠼⠃⠠⠎⠠⠕⠢⠼⠙⠐⠖⠼⠃⠠⠓⠢⠼⠃⠠⠕")?; return Ok(()); } @@ -1872,7 +2685,7 @@ fn chem_16_5_2() -> Result<()> { let expr = r#" N2 - Haber processH2 + Haber processH2 N H3 @@ -1952,7 +2765,7 @@ fn chem_16_5_4() -> Result<()> { - + @@ -1983,7 +2796,7 @@ fn chem_16_5_4() -> Result<()> { - + @@ -1995,7 +2808,7 @@ fn chem_16_5_4() -> Result<()> { - + @@ -2005,7 +2818,7 @@ fn chem_16_5_4() -> Result<()> { - + @@ -2040,7 +2853,7 @@ fn chem_16_5_4() -> Result<()> { - + @@ -2052,7 +2865,7 @@ fn chem_16_5_4() -> Result<()> { - + @@ -2111,16 +2924,16 @@ fn chem_16_5_5() -> Result<()> { - + - + - + - + @@ -2128,7 +2941,7 @@ fn chem_16_5_5() -> Result<()> { "#; - test_braille("UEB", expr, "⠰⠰⠰⠠⠏⠃⠔⠣⠐⠖⠐⠖⠜⠐⠖⠼⠃⠰⠑⠀⠘⠸⠶⠀⠠⠏⠃⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠠⠏⠃⠔⠣⠐⠖⠐⠖⠜⠐⠖⠼⠃⠰⠑⠀⠰⠘⠸⠶⠀⠠⠏⠃")?; return Ok(()); } diff --git a/tests/braille/UEB/other.rs b/tests/braille/UEB/other.rs index 7dda38f6c..9b8a1ab29 100644 --- a/tests/braille/UEB/other.rs +++ b/tests/braille/UEB/other.rs @@ -12,7 +12,7 @@ use anyhow::Result; #[test] fn overscript_grouping_aph_5_4_8() -> Result<()> { // this test was added because #220 (failed to add grouping around overscript) - let expr = " MN "; + let expr = " MN "; test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠍⠝⠜⠨⠔⠳⠺⠗⠕")?; return Ok(()); @@ -39,7 +39,7 @@ fn blank_aph_7_1_ex5() -> Result<()> { #[test] fn word_symbol_aph_10_3_11() -> Result<()> { // this test was added because ⊻ (U+22bb) uses a 'G1 Word mode' char, so is different than others - let expr = " p q "; + let expr = " p q "; test_braille("UEB", expr, "⠰⠰⠏⠈⠖⠠⠱⠟")?; return Ok(()); @@ -247,6 +247,23 @@ fn ueb_no_italic_typeform_for_math_letters() -> Result<()> { return Ok(()); } +#[test] +fn ueb_space_because_and_qed() -> Result<()> { + // ∵ and ∎ are spaced like ∴ (GTM 11 miscellaneous; same mo AddSpaces path as 1.7.9). + test_braille("UEB", "x=1", "⠈⠌⠀⠰⠭⠀⠐⠶⠀⠼⠁")?; + test_braille("UEB", "x=1", "⠰⠭⠀⠐⠶⠀⠼⠁⠀⠸⠫⠼⠙⠱")?; + return Ok(()); +} + +#[test] +fn ueb_math_typeform_not_part_of_word() -> Result<()> { + // Double-struck/script/fraktur letters are math alphabets, not part of an English + // letters-sequence, so a following whole word still takes its contraction (time → ⠐⠞). + test_braille("UEB", "Rtime", "⠈⠆⠠⠗⠐⠞")?; + test_braille("UEB", "R", "⠈⠆⠰⠠⠗")?; + return Ok(()); +} + #[test] fn ueb_italic_typeform_for_digits() -> Result<()> { // UEB uses italic typeform for digits (GTM 2.7 emphasis of numeric material).