# Generates Rust tables that define Unicode "script classes" for the purposes
# of autohinting.
#
# For performance, we want to link various pieces of data by index. For ease of
# modification and to avoid errors, we want to define those links symbolically
# by name. Thus, this script exists which converts symbolic references to
# indices when generating code.
#
# The bottom of this file contains the Rust generation code.

# Based on FreeType autofit coverage:
# https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/afcover.h
STYLE_FEATURES = [
    {
        "name": "petite capitals from capitals",
        "tag": "c2cp",
    },
    {
        "name": "small capitals from capitals",
        "tag": "c2sc",
    },
    {
        "name": "ordinals",
        "tag": "ordn",
    },
    {
        "name": "petite capitals",
        "tag": "pcap",
    },
    {
        "name": "ruby",
        "tag": "ruby",
    },
    {
        "name": "scientific inferiors",
        "tag": "sinf",
    },
    {
        "name": "small capitals",
        "tag": "smcp",
    },
    {
        "name": "subscript",
        "tag": "subs",
    },
    {
        "name": "superscript",
        "tag": "sups",
    },
    {
        "name": "titling",
        "tag": "titl",
    },
]

# Scripts that generate styles with the extended feature set above
# FreeType refers to these as "meta latin"
SCRIPTS_WITH_FEATURES = ["CYRL", "GREK", "LATN"]

# In relation to FreeType, this combines the AF_ScriptClass,
# AF_Script_UniRangeRec and AF_BlueStringset.
# Script definitions: https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/afscript.h
# Unicode ranges: https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/afranges.c
# Blues: https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/afblue.h
SCRIPT_CLASSES = [
    {
        "name": "Adlam",
        "tag": "ADLM",
        "hint_top_to_bottom": False,
        "std_chars": "𞤌 𞤮", # 𞤌 𞤮
        "base_ranges": [
            (0x1E900, 0x1E95F), # Adlam
        ],
        "non_base_ranges": [
            (0x1D944, 0x1E94A),
        ],
        "blues": [
            ("𞤌 𞤅 𞤈 𞤏 𞤔 𞤚", "TOP"),
            ("𞤂 𞤖", "0"),
            ("𞤬 𞤮 𞤻 𞤼 𞤾", "TOP | X_HEIGHT"),
            ("𞤤 𞤨 𞤩 𞤭 𞤴 𞤸 𞤺 𞥀", "0"),
        ],
    },
    {
        "name": "Arabic",
        "tag": "ARAB",
        "hint_top_to_bottom": False,
        "std_chars": "ل ح ـ", # ل ح ـ
        "base_ranges": [
            (0x0600, 0x06FF), # Arabic
            (0x0750, 0x07FF), # Arabic Supplement
            (0x08A0, 0x08FF), # Arabic Extended-A
            (0xFB50, 0xFDFF), # Arabic Presentation Forms-A
            (0xFE70, 0xFEFF), # Arabic Presentation Forms-B
            (0x1EE00, 0x1EEFF), # Arabic Mathematical Alphabetic Symbols
        ],
        "non_base_ranges": [
            (0x0600, 0x0605),
            (0x0610, 0x061A),
            (0x064B, 0x065F),
            (0x0670, 0x0670),
            (0x06D6, 0x06DC),
            (0x06DF, 0x06E4),
            (0x06E7, 0x06E8),
            (0x06EA, 0x06ED),
            (0x08D4, 0x08E1),
            (0x08D3, 0x08FF),
            (0xFBB2, 0xFBC1),
            (0xFE70, 0xFE70),
            (0xFE72, 0xFE72),
            (0xFE74, 0xFE74),
            (0xFE76, 0xFE76),
            (0xFE78, 0xFE78),
            (0xFE7A, 0xFE7A),
            (0xFE7C, 0xFE7C),
            (0xFE7E, 0xFE7E),
        ],
        "blues": [
            ("ا إ ل ك ط ظ", "TOP"),
            ("ت ث ط ظ ك", "0"),
            ("ـ", "NEUTRAL"),
        ],
    },
    {
        "name": "Armenian",
        "tag": "ARMN",
        "hint_top_to_bottom": False,
        "std_chars": "ս Ս", # ս Ս
        "base_ranges": [
            (0x0530, 0x058F), # Armenian
            (0xFB13, 0xFB17), # Alphab. Present. Forms (Armenian)
        ],
        "non_base_ranges": [
            (0x0559, 0x055F),
        ],
        "blues": [
            ("Ա Մ Ւ Ս Բ Գ Դ Օ", "TOP"),
            ("Ւ Ո Դ Ճ Շ Ս Տ Օ", "0"),
            ("ե է ի մ վ ֆ ճ", "TOP"),
            ("ա յ ւ ս գ շ ր օ", "TOP | X_HEIGHT"),
            ("հ ո ճ ա ե ծ ս օ", "0"),
            ("բ ը ի լ ղ պ փ ց", "0"),
        ],
    },
    {
        "name": "Avestan",
        "tag": "AVST",
        "hint_top_to_bottom": False,
        "std_chars": "𐬚", # 𐬚
        "base_ranges": [
            (0x10B00, 0x10B3F), # Avestan
        ],
        "non_base_ranges": [
            (0x10B39, 0x10B3F),
        ],
        "blues": [
            ("𐬀 𐬁 𐬐 𐬛", "TOP"),
            ("𐬀 𐬁", "0"),
        ],
    },
    {
        "name": "Bamum",
        "tag": "BAMU",
        "hint_top_to_bottom": False,
        "std_chars": "ꛁ ꛯ", # ꛁ ꛯ
        "base_ranges": [
            (0xA6A0, 0xA6FF), # Bamum
            # This is commented out in FreeType
            # (0x16800, 0x16A3F), # Bamum Supplement
        ],
        "non_base_ranges": [
            (0xA6F0, 0xA6F1),
        ],
        "blues": [
            ("ꚧ ꚨ ꛛ ꛉ ꛁ ꛈ ꛫ ꛯ", "TOP"),
            ("ꚭ ꚳ ꚶ ꛬ ꚢ ꚽ ꛯ ꛲", "0"),
        ],
    },
    {
        "name": "Bengali",
        "tag": "BENG",
        "hint_top_to_bottom": True,
        "std_chars": "০ ৪", # ০ ৪
        "base_ranges": [
            (0x0980, 0x09FF), # Bengali
        ],
        "non_base_ranges": [
            (0x0981, 0x0981),
            (0x09BC, 0x09BC),
            (0x09C1, 0x09C4),
            (0x09CD, 0x09CD),
            (0x09E2, 0x09E3),
            (0x09FE, 0x09FE),
        ],
        "blues": [
            ("ই ট ঠ ি ী ৈ ৗ", "TOP"),
            ("ও এ ড ত ন ব ল ক", "TOP"),
            ("অ ড ত ন ব ভ ল ক", "TOP | NEUTRAL | X_HEIGHT"),
            ("অ ড ত ন ব ভ ল ক", "0"),
        ],
    },
    {
        "name": "Buhid",
        "tag": "BUHD",
        "hint_top_to_bottom": False,
        "std_chars": "ᝋ ᝏ", # ᝋ ᝏ
        "base_ranges": [
            (0x1740, 0x175F), # Buhid
        ],
        "non_base_ranges": [
            (0x1752, 0x1753),
        ],
        "blues": [
            ("ᝐ ᝈ", "TOP"),
            ("ᝅ ᝊ ᝎ", "TOP"),
            ("ᝂ ᝃ ᝉ ᝌ", "TOP | X_HEIGHT"),
            ("ᝀ ᝃ ᝆ ᝉ ᝋ ᝏ ᝑ", "0"),
        ],
    },
    {
        "name": "Chakma",
        "tag": "CAKM",
        "hint_top_to_bottom": False,
        "std_chars": "𑄤 𑄉 𑄛", # 𑄤 𑄉 𑄛
        "base_ranges": [
            (0x11100, 0x1114F), # Chakma
        ],
        "non_base_ranges": [
            (0x11100, 0x11102),
            (0x11127, 0x11134),
            (0x11146, 0x11146),
        ],
        "blues": [
            ("𑄃 𑄅 𑄉 𑄙 𑄗", "TOP"),
            ("𑄅 𑄛 𑄝 𑄗 𑄓", "0"),
            ("𑄖𑄳𑄢 𑄘𑄳𑄢 𑄙𑄳𑄢 𑄤𑄳𑄢 𑄥𑄳𑄢", "0"),
        ],
    },
    {
        "name": "Canadian Syllabics",
        "tag": "CANS",
        "hint_top_to_bottom": False,
        "std_chars": "ᑌ ᓚ", # ᑌ ᓚ
        "base_ranges": [
            (0x1400, 0x167F), # Unified Canadian Aboriginal Syllabics
            (0x18B0, 0x18FF), # Unified Canadian Aboriginal Syllabics Extended
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("ᗜ ᖴ ᐁ ᒣ ᑫ ᑎ ᔑ ᗰ", "TOP"),
            ("ᗶ ᖵ ᒧ ᐃ ᑌ ᒍ ᔑ ᗢ", "0"),
            ("ᓓ ᓕ ᓀ ᓂ ᓄ ᕄ ᕆ ᘣ", "TOP | X_HEIGHT"),
            ("ᕃ ᓂ ᓀ ᕂ ᓗ ᓚ ᕆ ᘣ", "0"),
            ("ᐪ ᙆ ᣘ ᐢ ᒾ ᣗ ᔆ", "TOP"),
            ("ᙆ ᗮ ᒻ ᐞ ᔆ ᒡ ᒢ ᓑ", "0"),
        ],
    },
    {
        "name": "Carian",
        "tag": "CARI",
        "hint_top_to_bottom": False,
        "std_chars": "𐊫 𐋉", # 𐊫 𐋉
        "base_ranges": [
            (0x102A0, 0x102DF), # Carian
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐊧 𐊫 𐊬 𐊭 𐊱 𐊺 𐊼 𐊿", "TOP"),
            ("𐊣 𐊧 𐊷 𐋀 𐊫 𐊸 𐋉", "0"),
        ],
    },
    {
        "name": "Cherokee",
        "tag": "CHER",
        "hint_top_to_bottom": False,
        "std_chars": "Ꭴ Ꮕ ꮕ", # Ꭴ Ꮕ ꮕ
        "base_ranges": [
            (0x13A0, 0x13FF), # Cherokee
            (0xAB70, 0xABBF), # Cherokee Supplement
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("Ꮖ Ꮋ Ꭼ Ꮓ Ꭴ Ꮳ Ꭶ Ꮥ", "TOP"),
            ("Ꮖ Ꮋ Ꭼ Ꮓ Ꭴ Ꮳ Ꭶ Ꮥ", "0"),
            ("ꮒ ꮤ ꮶ ꭴ ꭾ ꮗ ꮝ ꮿ", "TOP"),
            ("ꮖ ꭼ ꮓ ꮠ ꮳ ꭶ ꮥ ꮻ", "TOP | X_HEIGHT"),
            ("ꮖ ꭼ ꮓ ꮠ ꮳ ꭶ ꮥ ꮻ", "0"),
            ("ᏸ ꮐ ꭹ ꭻ", "0"),
        ],
    },
    {
        "name": "Coptic",
        "tag": "COPT",
        "hint_top_to_bottom": False,
        "std_chars": "Ⲟ ⲟ", # Ⲟ ⲟ
        "base_ranges": [
            (0x2C80, 0x2CFF), # Coptic
        ],
        "non_base_ranges": [
            (0x2CEF, 0x2CF1),
        ],
        "blues": [
            ("Ⲍ Ⲏ Ⲡ Ⳟ Ⲟ Ⲑ Ⲥ Ⳋ", "TOP"),
            ("Ⳑ Ⳙ Ⳟ Ⲏ Ⲟ Ⲑ Ⳝ Ⲱ", "0"),
            ("ⲍ ⲏ ⲡ ⳟ ⲟ ⲑ ⲥ ⳋ", "TOP | X_HEIGHT"),
            ("ⳑ ⳙ ⳟ ⲏ ⲟ ⲑ ⳝ Ⳓ", "0"),
        ],
    },
    {
        "name": "Cypriot",
        "tag": "CPRT",
        "hint_top_to_bottom": False,
        "std_chars": "𐠅 𐠣", # 𐠅 𐠣
        "base_ranges": [
            (0x10800, 0x1083F), # Cypriot
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐠍 𐠙 𐠳 𐠱 𐠅 𐠓 𐠣 𐠦", "TOP"),
            ("𐠃 𐠊 𐠛 𐠣 𐠳 𐠵 𐠐", "0"),
            ("𐠈 𐠏 𐠖", "TOP"),
            ("𐠈 𐠏 𐠖", "0"),
        ],
    },
    {
        "name": "Cyrillic",
        "tag": "CYRL",
        "hint_top_to_bottom": False,
        "std_chars": "о О", # о О
        "base_ranges": [
            (0x0400, 0x04FF), # Cyrillic
            (0x0500, 0x052F), # Cyrillic Supplement
            (0x2DE0, 0x2DFF), # Cyrillic Extended-A
            (0xA640, 0xA69F), # Cyrillic Extended-B
            (0x1C80, 0x1C8F), # Cyrillic Extended-C
        ],
        "non_base_ranges": [
            (0x0483, 0x0489),
            (0x2DE0, 0x2DFF),
            (0xA66F, 0xA67F),
            (0xA69E, 0xA69F),
        ],
        "blues": [
            ("Б В Е П З О С Э", "TOP"),
            ("Б В Е Ш З О С Э", "0"),
            ("х п н ш е з о с", "TOP | X_HEIGHT"),
            ("х п н ш е з о с", "0"),
            ("р у ф", "0"),
        ],
    },
    {
        "name": "Devanagari",
        "tag": "DEVA",
        "hint_top_to_bottom": True,
        "std_chars": "ठ व ट", # ठ व ट
        "base_ranges": [
            (0x0900, 0x093B), # Devanagari
            (0x093D, 0x0950), # ... continued
            (0x0953, 0x0963), # ... continued
            (0x0966, 0x097F), # ... continued
            (0x20B9, 0x20B9), # (new) Rupee sign
            (0xA8E0, 0xA8FF), # Devanagari Extended
        ],
        "non_base_ranges": [
            (0x0900, 0x0902),
            (0x093A, 0x093A),
            (0x0941, 0x0948),
            (0x094D, 0x094D),
            (0x0953, 0x0957),
            (0x0962, 0x0963),
            (0xA8E0, 0xA8F1),
            (0xA8FF, 0xA8FF),
        ],
        "blues": [
            ("ई ऐ ओ औ ि ी ो ौ", "TOP"),
            ("क म अ आ थ ध भ श", "TOP"),
            ("क न म उ छ ट ठ ड", "TOP | NEUTRAL | X_HEIGHT"),
            ("क न म उ छ ट ठ ड", "0"),
            ("ु ृ", "0"),
        ],
    },
    {
        "name": "Deseret",
        "tag": "DSRT",
        "hint_top_to_bottom": False,
        "std_chars": "𐐄 𐐬", # 𐐄 𐐬
        "base_ranges": [
            (0x10400, 0x1044F), # Deseret
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐐂 𐐄 𐐋 𐐗 𐐑", "TOP"),
            ("𐐀 𐐂 𐐄 𐐗 𐐛", "0"),
            ("𐐪 𐐬 𐐳 𐐿 𐐹", "TOP | X_HEIGHT"),
            ("𐐨 𐐪 𐐬 𐐿 𐑃", "0"),
        ],
    },
    {
        "name": "Ethiopic",
        "tag": "ETHI",
        "hint_top_to_bottom": False,
        "std_chars": "ዐ", # ዐ
        "base_ranges": [
            (0x1200, 0x137F), # Ethiopic
            (0x1380, 0x139F), # Ethiopic Supplement
            (0x2D80, 0x2DDF), # Ethiopic Extended
            (0xAB00, 0xAB2F), # Ethiopic Extended-A
        ],
        "non_base_ranges": [
            (0x135D, 0x135F),
        ],
        "blues": [
            ("ሀ ሃ ዘ ፐ ማ በ ዋ ዐ", "TOP"),
            ("ለ ሐ በ ዘ ሀ ሪ ዐ ጨ", "0"),
        ],
    },
    {
        "name": "Georgian (Mkhedruli)",
        "tag": "GEOR",
        "hint_top_to_bottom": False,
        "std_chars": "ი ე ა Ჿ", # ი ე ა Ი
        "base_ranges": [
            (0x10D0, 0x10FF), # Georgian (Mkhedruli)
            (0x1C90, 0x1CBF), # Georgian Extended (Mtavruli)
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("გ დ ე ვ თ ი ო ღ", "TOP | X_HEIGHT"),
            ("ა ზ მ ს შ ძ ხ პ", "0"),
            ("ს ხ ქ ზ მ შ ჩ წ", "TOP"),
            ("ე ვ ჟ ტ უ ფ ქ ყ", "0"),
            ("Ნ Ჟ Ჳ Ჸ Გ Ე Ო Ჴ", "TOP"),
            ("Ი Ჲ Ო Ჩ Მ Შ Ჯ Ჽ", "0"),
        ],
    },
    {
        "name": "Georgian (Khutsuri)",
        "tag": "GEOK",
        "hint_top_to_bottom": False,
        "std_chars": "Ⴖ Ⴑ ⴙ", # Ⴖ Ⴑ ⴙ
        "base_ranges": [
            (0x10A0, 0x10CD), # Georgian (Asomtavruli)
            (0x2D00, 0x2D2D), # Georgian Supplement (Nuskhuri)
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("Ⴑ Ⴇ Ⴙ Ⴜ Ⴄ Ⴅ Ⴓ Ⴚ", "TOP"),
            ("Ⴄ Ⴅ Ⴇ Ⴈ Ⴆ Ⴑ Ⴊ Ⴋ", "0"),
            ("ⴁ ⴗ ⴂ ⴄ ⴅ ⴇ ⴔ ⴖ", "TOP | X_HEIGHT"),
            ("ⴈ ⴌ ⴖ ⴎ ⴃ ⴆ ⴋ ⴢ", "0"),
            ("ⴐ ⴑ ⴓ ⴕ ⴙ ⴛ ⴡ ⴣ", "TOP"),
            ("ⴄ ⴅ ⴔ ⴕ ⴁ ⴂ ⴘ ⴝ", "0"),
        ],
    },
    {
        "name": "Glagolitic",
        "tag": "GLAG",
        "hint_top_to_bottom": False,
        "std_chars": "Ⱅ ⱅ", # Ⱅ ⱅ
        "base_ranges": [
            (0x2C00, 0x2C5F), # Glagolitic
            (0x1E000, 0x1E02F), # Glagolitic Supplement
        ],
        "non_base_ranges": [
            (0x1E000, 0x1E02F),
        ],
        "blues": [
            ("Ⰵ Ⱄ Ⱚ Ⰴ Ⰲ Ⰺ Ⱛ Ⰻ", "TOP"),
            ("Ⰵ Ⰴ Ⰲ Ⱚ Ⱎ Ⱑ Ⰺ Ⱄ", "0"),
            ("ⰵ ⱄ ⱚ ⰴ ⰲ ⰺ ⱛ ⰻ", "TOP | X_HEIGHT"),
            ("ⰵ ⰴ ⰲ ⱚ ⱎ ⱑ ⰺ ⱄ", "0"),
        ],
    },
    {
        "name": "Gothic",
        "tag": "GOTH",
        "hint_top_to_bottom": True,
        "std_chars": "𐌴 𐌾 𐍃", # 𐌴 𐌾 𐍃
        "base_ranges": [
            (0x10330, 0x1034F), # Gothic
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐌲 𐌶 𐍀 𐍄 𐌴 𐍃 𐍈 𐌾", "TOP"),
            ("𐌶 𐌴 𐍃 𐍈", "0"),
        ],
    },
    {
        "name": "Greek",
        "tag": "GREK",
        "hint_top_to_bottom": False,
        "std_chars": "ο Ο", # ο Ο
        "base_ranges": [
            (0x0370, 0x03FF), # Greek and Coptic
            (0x1F00, 0x1FFF), # Greek Extended
        ],
        "non_base_ranges": [
            (0x037A, 0x037A),
            (0x0384, 0x0385),
            (0x1FBD, 0x1FC1),
            (0x1FCD, 0x1FCF),
            (0x1FDD, 0x1FDF),
            (0x1FED, 0x1FEF),
            (0x1FFD, 0x1FFE),
        ],
        "blues": [
            ("Γ Β Ε Ζ Θ Ο Ω", "TOP"),
            ("Β Δ Ζ Ξ Θ Ο", "0"),
            ("β θ δ ζ λ ξ", "TOP"),
            ("α ε ι ο π σ τ ω", "TOP | X_HEIGHT"),
            ("α ε ι ο π σ τ ω", "0"),
            ("β γ η μ ρ φ χ ψ", "0"),
        ],
    },
    {
        "name": "Gujarati",
        "tag": "GUJR",
        "hint_top_to_bottom": False,
        "std_chars": "ટ ૦", # ટ ૦
        "base_ranges": [
            (0x0A80, 0x0AFF), # Gujarati
        ],
        "non_base_ranges": [
            (0x0A81, 0x0A82),
            (0x0ABC, 0x0ABC),
            (0x0AC1, 0x0AC8),
            (0x0ACD, 0x0ACD),
            (0x0AE2, 0x0AE3),
            (0x0AFA, 0x0AFF),
        ],
        "blues": [
            ("ત ન ઋ ઌ છ ટ ર ૦", "TOP | X_HEIGHT"),
            ("ખ ગ ઘ ઞ ઇ ઈ ઠ જ", "0"),
            ("ઈ ઊ િ ી લી શ્ચિ જિ સી", "TOP"),
            ("ુ ૃ ૄ ખુ છૃ છૄ", "0"),
            ("૦ ૧ ૨ ૩ ૭", "TOP"),
        ],
    },
    {
        "name": "Gurmukhi",
        "tag": "GURU",
        "hint_top_to_bottom": True,
        "std_chars": "ਠ ਰ ੦", # ਠ ਰ ੦
        "base_ranges": [
            (0x0A00, 0x0A7F), # Gurmukhi
        ],
        "non_base_ranges": [
            (0x0A01, 0x0A02),
            (0x0A3C, 0x0A3C),
            (0x0A41, 0x0A51),
            (0x0A70, 0x0A71),
            (0x0A75, 0x0A75),
        ],
        "blues": [
            ("ਇ ਈ ਉ ਏ ਓ ੳ ਿ ੀ", "TOP"),
            ("ਕ ਗ ਙ ਚ ਜ ਤ ਧ ਸ", "TOP"),
            ("ਕ ਗ ਙ ਚ ਜ ਤ ਧ ਸ", "TOP | NEUTRAL | X_HEIGHT"),
            ("ਅ ਏ ਓ ਗ ਜ ਠ ਰ ਸ", "0"),
            ("੦ ੧ ੨ ੩ ੭", "TOP"),
        ],
    },
    {
        "name": "Hebrew",
        "tag": "HEBR",
        "hint_top_to_bottom": False,
        "std_chars": "ם", # ם
        "base_ranges": [
            (0x0590, 0x05FF), # Hebrew
            (0xFB1D, 0xFB4F), # Alphab. Present. Forms (Hebrew)
        ],
        "non_base_ranges": [
            (0x0591, 0x05BF),
            (0x05C1, 0x05C2),
            (0x05C4, 0x05C5),
            (0x05C7, 0x05C7),
            (0xFB1E, 0xFB1E),
        ],
        "blues": [
            ("ב ד ה ח ך כ ם ס", "TOP | LONG"),
            ("ב ט כ ם ס צ", "0"),
            ("ק ך ן ף ץ", "0"),
        ],
    },
    {
        "name": "Kayah Li",
        "tag": "KALI",
        "hint_top_to_bottom": False,
        "std_chars": "ꤍ ꤀", # ꤍ ꤀
        "base_ranges": [
            (0xA900, 0xA92F), # Kayah Li
        ],
        "non_base_ranges": [
            (0xA926, 0xA92D),
        ],
        "blues": [
            ("꤅ ꤏ ꤁ ꤋ ꤀ ꤍ", "TOP | X_HEIGHT"),
            ("꤈ ꤘ ꤀ ꤍ ꤢ", "0"),
            ("ꤖ ꤡ", "TOP"),
            ("ꤑ ꤜ ꤞ", "0"),
            ("ꤑ꤬ ꤜ꤭ ꤔ꤬", "0"),
        ],
    },
    {
        "name": "Khmer",
        "tag": "KHMR",
        "hint_top_to_bottom": False,
        "std_chars": "០", # ០
        "base_ranges": [
            (0x1780, 0x17FF), # Khmer
        ],
        "non_base_ranges": [
            (0x17B7, 0x17BD),
            (0x17C6, 0x17C6),
            (0x17C9, 0x17D3),
            (0x17DD, 0x17DD),
        ],
        "blues": [
            ("ខ ទ ន ឧ ឩ ា", "TOP | X_HEIGHT"),
            ("ក្ក ក្ខ ក្គ ក្ថ", "SUB_TOP"),
            ("ខ ឃ ច ឋ ប ម យ ឲ", "0"),
            ("ត្រ រៀ ឲ្យ អឿ", "0"),
            ("ន្ត្រៃ ង្ខ្យ ក្បៀ ច្រៀ ន្តឿ ល្បឿ", "0"),
        ],
    },
    {
        "name": "Khmer Symbols",
        "tag": "KHMS",
        "hint_top_to_bottom": False,
        "std_chars": "᧡ ᧪", # ᧡ ᧪
        "base_ranges": [
            (0x19E0, 0x19FF), # Khmer Symbols
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("᧠ ᧡", "TOP | X_HEIGHT"),
            ("᧶ ᧹", "0"),
        ],
    },
    {
        "name": "Kannada",
        "tag": "KNDA",
        "hint_top_to_bottom": False,
        "std_chars": "೦ ಬ", # ೦ ಬ
        "base_ranges": [
            (0x0C80, 0x0CFF), # Kannada
        ],
        "non_base_ranges": [
            (0x0C81, 0x0C81),
            (0x0CBC, 0x0CBC),
            (0x0CBF, 0x0CBF),
            (0x0CC6, 0x0CC6),
            (0x0CCC, 0x0CCD),
            (0x0CE2, 0x0CE3),
        ],
        "blues": [
            ("ಇ ಊ ಐ ಣ ಸಾ ನಾ ದಾ ರಾ", "TOP"),
            ("ಅ ಉ ಎ ಲ ೦ ೨ ೬ ೭", "0"),
        ],
    },
    {
        "name": "Lao",
        "tag": "LAOO",
        "hint_top_to_bottom": False,
        "std_chars": "໐", # ໐
        "base_ranges": [
            (0x0E80, 0x0EFF), # Lao
        ],
        "non_base_ranges": [
            (0x0EB1, 0x0EB1),
            (0x0EB4, 0x0EBC),
            (0x0EC8, 0x0ECD),
        ],
        "blues": [
            ("າ ດ ອ ມ ລ ວ ຣ ງ", "TOP | X_HEIGHT"),
            ("າ ອ ບ ຍ ຣ ຮ ວ ຢ", "0"),
            ("ປ ຢ ຟ ຝ", "TOP"),
            ("ໂ ໄ ໃ", "TOP"),
            ("ງ ຊ ຖ ຽ ໆ ຯ", "0"),
        ],
    },
    {
        "name": "Latin",
        "tag": "LATN",
        "hint_top_to_bottom": False,
        "std_chars": "o O 0",
        "base_ranges": [
            (0x0020, 0x007F), # Basic Latin (no control chars)
            (0x00A0, 0x00A9), # Latin-1 Supplement (no control chars)
            (0x00AB, 0x00B1), # ... continued
            (0x00B4, 0x00B8), # ... continued
            (0x00BB, 0x00FF), # ... continued
            (0x0100, 0x017F), # Latin Extended-A
            (0x0180, 0x024F), # Latin Extended-B
            (0x0250, 0x02AF), # IPA Extensions
            (0x02B9, 0x02DF), # Spacing Modifier Letters
            (0x02E5, 0x02FF), # ... continued
            (0x0300, 0x036F), # Combining Diacritical Marks
            (0x1AB0, 0x1ABE), # Combining Diacritical Marks Extended
            (0x1D00, 0x1D2B), # Phonetic Extensions
            (0x1D6B, 0x1D77), # ... continued
            (0x1D79, 0x1D7F), # ... continued
            (0x1D80, 0x1D9A), # Phonetic Extensions Supplement
            (0x1DC0, 0x1DFF), # Combining Diacritical Marks Supplement
            (0x1E00, 0x1EFF), # Latin Extended Additional
            (0x2000, 0x206F), # General Punctuation
            (0x20A0, 0x20B8), # Currency Symbols ...
            (0x20BA, 0x20CF), # ... except new Rupee sign
            (0x2150, 0x218F), # Number Forms
            (0x2C60, 0x2C7B), # Latin Extended-C
            (0x2C7E, 0x2C7F), # ... continued
            (0x2E00, 0x2E7F), # Supplemental Punctuation
            (0xA720, 0xA76F), # Latin Extended-D
            (0xA771, 0xA7F7), # ... continued
            (0xA7FA, 0xA7FF), # ... continued
            (0xAB30, 0xAB5B), # Latin Extended-E
            (0xAB60, 0xAB6F), # ... continued
            (0xFB00, 0xFB06), # Alphab. Present. Forms (Latin Ligs)
            (0x1D400, 0x1D7FF), # Mathematical Alphanumeric Symbols
        ],
        "non_base_ranges": [
            (0x005E, 0x0060),
            (0x007E, 0x007E),
            (0x00A8, 0x00A9),
            (0x00AE, 0x00B0),
            (0x00B4, 0x00B4),
            (0x00B8, 0x00B8),
            (0x00BC, 0x00BE),
            (0x02B9, 0x02DF),
            (0x02E5, 0x02FF),
            (0x0300, 0x036F),
            (0x1AB0, 0x1ABE),
            (0x1DC0, 0x1DFF),
            (0x2017, 0x2017),
            (0x203E, 0x203E),
            (0xA788, 0xA788),
            (0xA7F8, 0xA7FA),
        ],
        "blues": [
            ("T H E Z O C Q S", "TOP"),
            ("H E Z L O C U S", "0"),
            ("f i j k d b h", "TOP"),
            ("u v x z o e s c", "TOP | X_HEIGHT"),
            ("n r x z o e s c", "0"),
            ("p q g j y", "0"),
        ],
    },
    {
        "name": "Latin Subscript Fallback",
        "tag": "LATB",
        "hint_top_to_bottom": False,
        "std_chars": "ₒ ₀", # ₒ ₀
        "base_ranges": [
            (0x1D62, 0x1D6A), # some small subscript letters
            (0x2080, 0x209C), # subscript digits and letters
            (0x2C7C, 0x2C7C), # latin subscript small letter j
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("₀ ₃ ₅ ₇ ₈", "TOP"),
            ("₀ ₁ ₂ ₃ ₈", "0"),
            ("ᵢ ⱼ ₕ ₖ ₗ", "TOP"),
            ("ₐ ₑ ₒ ₓ ₙ ₛ ᵥ ᵤ ᵣ", "TOP | X_HEIGHT"),
            ("ₐ ₑ ₒ ₓ ₙ ₛ ᵥ ᵤ ᵣ", "0"),
            ("ᵦ ᵧ ᵨ ᵩ ₚ", "0"),
        ],
    },
    {
        "name": "Latin Superscript Fallback",
        "tag": "LATP",
        "hint_top_to_bottom": False,
        "std_chars": "ᵒ ᴼ ⁰", # ᵒ ᴼ ⁰
        "base_ranges": [
            (0x00AA, 0x00AA), # feminine ordinal indicator
            (0x00B2, 0x00B3), # superscript two and three
            (0x00B9, 0x00BA), # superscript one, masc. ord. indic.
            (0x02B0, 0x02B8), # some latin superscript mod. letters
            (0x02E0, 0x02E4), # some IPA modifier letters
            (0x1D2C, 0x1D61), # latin superscript modifier letters
            (0x1D78, 0x1D78), # modifier letter cyrillic en
            (0x1D9B, 0x1DBF), # more modifier letters
            (0x2070, 0x207F), # superscript digits and letters
            (0x2C7D, 0x2C7D), # modifier letter capital v
            (0xA770, 0xA770), # modifier letter us
            (0xA7F8, 0xA7F9), # more modifier letters
            (0xAB5C, 0xAB5F), # more modifier letters
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("⁰ ³ ⁵ ⁷ ᵀ ᴴ ᴱ ᴼ", "TOP"),
            ("⁰ ¹ ² ³ ᴱ ᴸ ᴼ ᵁ", "0"),
            ("ᵇ ᵈ ᵏ ʰ ʲ ᶠ ⁱ", "TOP"),
            ("ᵉ ᵒ ʳ ˢ ˣ ᶜ ᶻ", "TOP | X_HEIGHT"),
            ("ᵉ ᵒ ʳ ˢ ˣ ᶜ ᶻ", "0"),
            ("ᵖ ʸ ᵍ", "0"),
        ],
    },
    {
        "name": "Lisu",
        "tag": "LISU",
        "hint_top_to_bottom": False,
        "std_chars": "ꓳ", # ꓳ
        "base_ranges": [
            (0xA4D0, 0xA4FF), # Lisu
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("ꓡ ꓧ ꓱ ꓶ ꓩ ꓚ ꓵ ꓳ", "TOP"),
            ("ꓕ ꓜ ꓞ ꓡ ꓛ ꓢ ꓳ ꓴ", "0"),
        ],
    },
    {
        "name": "Malayalam",
        "tag": "MLYM",
        "hint_top_to_bottom": False,
        "std_chars": "ഠ റ", # ഠ റ
        "base_ranges": [
            (0x0D00, 0x0D7F), # Malayalam
        ],
        "non_base_ranges": [
            (0x0D00, 0x0D01),
            (0x0D3B, 0x0D3C),
            (0x0D4D, 0x0D4E),
            (0x0D62, 0x0D63),
        ],
        "blues": [
            ("ഒ ട ഠ റ ച പ ച്ച പ്പ", "TOP"),
            ("ട ഠ ധ ശ ഘ ച ഥ ല", "0"),
        ],
    },
    {
        "name": "Medefaidrin",
        "tag": "MEDF",
        "hint_top_to_bottom": False,
        "std_chars": "𖹡 𖹛 𖹯", # 𖹡 𖹛 𖹯
        "base_ranges": [
            (0x16E40, 0x16E9F), # Medefaidrin
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𖹀 𖹁 𖹂 𖹃 𖹏 𖹚 𖹟", "TOP"),
            ("𖹀 𖹁 𖹂 𖹃 𖹏 𖹚 𖹒 𖹓", "0"),
            ("𖹤 𖹬 𖹧 𖹴 𖹶 𖹾", "TOP"),
            ("𖹠 𖹡 𖹢 𖹹 𖹳 𖹮", "TOP | X_HEIGHT"),
            ("𖹠 𖹡 𖹢 𖹳 𖹭 𖹽", "0"),
            ("𖹥 𖹨 𖹩", "0"),
            ("𖺀 𖺅 𖺈 𖺄 𖺍", "TOP"),
        ],
    },
    {
        "name": "Mongolian",
        "tag": "MONG",
        "hint_top_to_bottom": True,
        "std_chars": "ᡂ ᠪ", # ᡂ ᠪ
        "base_ranges": [
            (0x1800, 0x18AF), # Mongolian
            (0x11660, 0x1167F), # Mongolian Supplement
        ],
        "non_base_ranges": [
            (0x1885, 0x1886),
            (0x18A9, 0x18A9),
        ],
        "blues": [
            # U+200D is escaped to avoid the presence of suspicious-looking
            # invisible chars in the generated Rust source.
            ("ᠳ ᠴ ᠶ ᠽ ᡂ ᡊ \\u{200d}ᡡ\\u{200d} \\u{200d}ᡳ\\u{200d}", "TOP"),
            ("ᡃ", "0"),
        ],
    },
    {
        "name": "Myanmar",
        "tag": "MYMR",
        "hint_top_to_bottom": False,
        "std_chars": "ဝ င ဂ", # ဝ င ဂ
        "base_ranges": [
            (0x1000, 0x109F), # Myanmar
            (0xA9E0, 0xA9FF), # Myanmar Extended-B
            (0xAA60, 0xAA7F), # Myanmar Extended-A
        ],
        "non_base_ranges": [
            (0x102D, 0x1030),
            (0x1032, 0x1037),
            (0x103A, 0x103A),
            (0x103D, 0x103E),
            (0x1058, 0x1059),
            (0x105E, 0x1060),
            (0x1071, 0x1074),
            (0x1082, 0x1082),
            (0x1085, 0x1086),
            (0x108D, 0x108D),
            (0xA9E5, 0xA9E5),
            (0xAA7C, 0xAA7C),
        ],
        "blues": [
            ("ခ ဂ င ဒ ဝ ၥ ၊ ။", "TOP | X_HEIGHT"),
            ("င ဎ ဒ ပ ဗ ဝ ၊ ။", "0"),
            ("ဩ ြ ၍ ၏ ၆ ါ ိ", "TOP"),
            ("ဉ ည ဥ ဩ ဨ ၂ ၅ ၉", "0"),
        ],
    },
    {
        "name": "N'Ko",
        "tag": "NKOO",
        "hint_top_to_bottom": False,
        "std_chars": "ߋ ߀", # ߋ ߀
        "base_ranges": [
            (0x07C0, 0x07FF), # N'Ko
        ],
        "non_base_ranges": [
            (0x07EB, 0x07F5),
            (0x07FD, 0x07FD),
        ],
        "blues": [
            ("ߐ ߉ ߒ ߟ ߖ ߜ ߠ ߥ", "TOP"),
            ("߀ ߘ ߡ ߠ ߥ", "0"),
            ("ߏ ߛ ߋ", "TOP | X_HEIGHT"),
            ("ߎ ߏ ߛ ߋ", "0"),
        ],
    },
    {
        "name": "no script",
        "tag": "NONE",
        "hint_top_to_bottom": False,
        "std_chars": "",
        "base_ranges": [
        ],
        "non_base_ranges": [
        ],
        "blues": [
        ],
    },
    {
        "name": "Ol Chiki",
        "tag": "OLCK",
        "hint_top_to_bottom": False,
        "std_chars": "ᱛ", # ᱛ
        "base_ranges": [
            (0x1C50, 0x1C7F), # Ol Chiki
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("ᱛ ᱜ ᱝ ᱡ ᱢ ᱥ", "TOP"),
            ("ᱛ ᱜ ᱝ ᱡ ᱢ ᱥ", "0"),
        ],
    },
    {
        "name": "Old Turkic",
        "tag": "ORKH",
        "hint_top_to_bottom": False,
        "std_chars": "𐰗", # 𐰗
        "base_ranges": [
            (0x10C00, 0x10C4F), # Old Turkic
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐰗 𐰘 𐰧", "TOP"),
            ("𐰉 𐰗 𐰦 𐰧", "0"),
        ],
    },
    {
        "name": "Osage",
        "tag": "OSGE",
        "hint_top_to_bottom": False,
        "std_chars": "𐓂 𐓪", # 𐓂 𐓪
        "base_ranges": [
            (0x104B0, 0x104FF), # Osage
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐒾 𐓍 𐓒 𐓓 𐒻 𐓂 𐒵 𐓆", "TOP"),
            ("𐒰 𐓍 𐓂 𐒿 𐓎 𐒹", "0"),
            ("𐒼 𐒽 𐒾", "0"),
            ("𐓵 𐓶 𐓺 𐓻 𐓝 𐓣 𐓪 𐓮", "TOP | X_HEIGHT"),
            ("𐓘 𐓚 𐓣 𐓵 𐓡 𐓧 𐓪 𐓶", "0"),
            ("𐓤 𐓦 𐓸 𐓹 𐓛", "TOP"),
            ("𐓤 𐓥 𐓦", "0"),
        ],
    },
    {
        "name": "Osmanya",
        "tag": "OSMA",
        "hint_top_to_bottom": False,
        "std_chars": "𐒆 𐒠", # 𐒆 𐒠
        "base_ranges": [
            (0x10480, 0x104AF), # Osmanya
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐒆 𐒉 𐒐 𐒒 𐒘 𐒛 𐒠 𐒣", "TOP"),
            ("𐒀 𐒂 𐒆 𐒈 𐒊 𐒒 𐒠 𐒩", "0"),
        ],
    },
    {
        "name": "Hanifi Rohingya",
        "tag": "ROHG",
        "hint_top_to_bottom": False,
        "std_chars": "𐴰", # 𐴰
        "base_ranges": [
            (0x10D00, 0x10D3F), # Hanifi Rohingya
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐴃 𐴀 𐴆 𐴖 𐴕", "TOP"),
            ("𐴔 𐴖 𐴕 𐴑 𐴐", "0"),
            ("ـ", "NEUTRAL"),
        ],
    },
    {
        "name": "Saurashtra",
        "tag": "SAUR",
        "hint_top_to_bottom": False,
        "std_chars": "ꢝ ꣐", # ꢝ ꣐
        "base_ranges": [
            (0xA880, 0xA8DF), # Saurashtra
        ],
        "non_base_ranges": [
            (0xA880, 0xA881),
            (0xA8B4, 0xA8C5),
        ],
        "blues": [
            ("ꢜ ꢞ ꢳ ꢂ ꢖ ꢒ ꢝ ꢛ", "TOP"),
            ("ꢂ ꢨ ꢺ ꢤ ꢎ", "0"),
        ],
    },
    {
        "name": "Shavian",
        "tag": "SHAW",
        "hint_top_to_bottom": False,
        "std_chars": "𐑴", # 𐑴
        "base_ranges": [
            (0x10450, 0x1047F), # Shavian
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("𐑕 𐑙", "TOP"),
            ("𐑔 𐑖 𐑗 𐑹 𐑻", "0"),
            ("𐑟 𐑣", "0"),
            ("𐑱 𐑲 𐑳 𐑴 𐑸 𐑺 𐑼", "TOP | X_HEIGHT"),
            ("𐑴 𐑻 𐑹", "0"),
        ],
    },
    {
        "name": "Sinhala",
        "tag": "SINH",
        "hint_top_to_bottom": False,
        "std_chars": "ට", # ට
        "base_ranges": [
            (0x0D80, 0x0DFF), # Sinhala
        ],
        "non_base_ranges": [
            (0x0DCA, 0x0DCA),
            (0x0DD2, 0x0DD6),
        ],
        "blues": [
            ("ඉ ක ඝ ඳ ප ය ල ෆ", "TOP"),
            ("එ ඔ ඝ ජ ට ථ ධ ර", "0"),
            ("ද ඳ උ ල තූ තු බු දු", "0"),
        ],
    },
    {
        "name": "Sundanese",
        "tag": "SUND",
        "hint_top_to_bottom": False,
        "std_chars": "᮰", # ᮰
        "base_ranges": [
            (0x1B80, 0x1BBF), # Sundanese
            (0x1CC0, 0x1CCF), # Sundanese Supplement
        ],
        "non_base_ranges": [
            (0x1B80, 0x1B82),
            (0x1BA1, 0x1BAD),
        ],
        "blues": [
            ("ᮋ ᮞ ᮮ ᮽ ᮰ ᮈ", "TOP"),
            ("ᮄ ᮔ ᮕ ᮗ ᮰ ᮆ ᮈ ᮉ", "0"),
            ("ᮼ ᳄", "0"),
        ],
    },
    {
        "name": "Tamil",
        "tag": "TAML",
        "hint_top_to_bottom": False,
        "std_chars": "௦", # ௦
        "base_ranges": [
            (0x0B80, 0x0BFF), # Tamil
        ],
        "non_base_ranges": [
            (0x0B82, 0x0B82),
            (0x0BC0, 0x0BC2),
            (0x0BCD, 0x0BCD),
        ],
        "blues": [
            ("உ ஒ ஓ ற ஈ க ங ச", "TOP"),
            ("க ச ல ஶ உ ங ட ப", "0"),
        ],
    },
    {
        "name": "Tai Viet",
        "tag": "TAVT",
        "hint_top_to_bottom": False,
        "std_chars": "ꪒ ꪫ", # ꪒ ꪫ
        "base_ranges": [
            (0xAA80, 0xAADF), # Tai Viet
        ],
        "non_base_ranges": [
            (0xAAB0, 0xAAB0),
            (0xAAB2, 0xAAB4),
            (0xAAB7, 0xAAB8),
            (0xAABE, 0xAABF),
            (0xAAC1, 0xAAC1),
        ],
        "blues": [
            ("ꪆ ꪔ ꪒ ꪖ ꪫ", "TOP"),
            ("ꪉ ꪫ ꪮ", "0"),
        ],
    },
    {
        "name": "Telugu",
        "tag": "TELU",
        "hint_top_to_bottom": False,
        "std_chars": "౦ ౧", # ౦ ౧
        "base_ranges": [
            (0x0C00, 0x0C7F), # Telugu
        ],
        "non_base_ranges": [
            (0x0C00, 0x0C00),
            (0x0C04, 0x0C04),
            (0x0C3E, 0x0C40),
            (0x0C46, 0x0C56),
            (0x0C62, 0x0C63),
        ],
        "blues": [
            ("ఇ ఌ ఙ ఞ ణ ఱ ౯", "TOP"),
            ("అ క చ ర ఽ ౨ ౬", "0"),
        ],
    },
    {
        "name": "Tifinagh",
        "tag": "TFNG",
        "hint_top_to_bottom": False,
        "std_chars": "ⵔ", # ⵔ
        "base_ranges": [
            (0x2D30, 0x2D7F), # Tifinagh
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("ⵔ ⵙ ⵛ ⵞ ⴵ ⴼ ⴹ ⵎ", "TOP"),
            ("ⵔ ⵙ ⵛ ⵞ ⴵ ⴼ ⴹ ⵎ", "0"),
        ],
    },
    {
        "name": "Thai",
        "tag": "THAI",
        "hint_top_to_bottom": False,
        "std_chars": "า ๅ ๐", # า ๅ ๐
        "base_ranges": [
            (0x0E00, 0x0E7F), # Thai
        ],
        "non_base_ranges": [
            (0x0E31, 0x0E31),
            (0x0E34, 0x0E3A),
            (0x0E47, 0x0E4E),
        ],
        "blues": [
            ("บ เ แ อ ก า", "TOP | X_HEIGHT"),
            ("บ ป ษ ฯ อ ย ฮ", "0"),
            ("ป ฝ ฟ", "TOP"),
            ("โ ใ ไ", "TOP"),
            ("ฎ ฏ ฤ ฦ", "0"),
            ("ญ ฐ", "0"),
            ("๐ ๑ ๓", "0"),
        ],
    },
    {
        "name": "Vai",
        "tag": "VAII",
        "hint_top_to_bottom": False,
        "std_chars": "ꘓ ꖜ ꖴ", # ꘓ ꖜ ꖴ
        "base_ranges": [
            (0xA500, 0xA63F), # Vai
        ],
        "non_base_ranges": [
        ],
        "blues": [
            ("ꗍ ꘖ ꘙ ꘜ ꖜ ꖝ ꔅ ꕢ", "TOP"),
            ("ꗍ ꘖ ꘙ ꗞ ꔅ ꕢ ꖜ ꔆ", "0"),
        ],
    },
    {
        "name": "Limbu",
        "tag": "LIMB",
        "hint_top_to_bottom": False,
        "std_chars": "o", # XXX
        "base_ranges": [
            (0x1900, 0x194F), # Limbu
        ],
        "non_base_ranges": [
            (0x1920, 0x1922),
            (0x1927, 0x1934),
            (0x1937, 0x193B),
        ],
        "blues": [],
    },
    {
        "name": "Oriya",
        "tag": "ORYA",
        "hint_top_to_bottom": False,
        "std_chars": "o", # XXX
        "base_ranges": [
            (0x0B00, 0x0B7F), # Oriya
        ],
        "non_base_ranges": [
            (0x0B01, 0x0B02),
            (0x0B3C, 0x0B3C),
            (0x0B3F, 0x0B3F),
            (0x0B41, 0x0B44),
            (0x0B4D, 0x0B56),
            (0x0B62, 0x0B63),
        ],
        "blues": [],
    },
    {
        "name": "Syloti Nagri",
        "tag": "SYLO",
        "hint_top_to_bottom": False,
        "std_chars": "o", # XXX
        "base_ranges": [
            (0xA800, 0xA82F), # Syloti Nagri
        ],
        "non_base_ranges": [
            (0xA802, 0xA802),
            (0xA806, 0xA806),
            (0xA80B, 0xA80B),
            (0xA825, 0xA826),
        ],
        "blues": [],
    },
    {
        "name": "Tibetan",
        "tag": "TIBT",
        "hint_top_to_bottom": False,
        "std_chars": "o", # XXX
        "base_ranges": [
            (0x0F00, 0x0FFF), # Tibetan
        ],
        "non_base_ranges": [
            (0x0F18, 0x0F19),
            (0x0F35, 0x0F35),
            (0x0F37, 0x0F37),
            (0x0F39, 0x0F39),
            (0x0F3E, 0x0F3F),
            (0x0F71, 0x0F7E),
            (0x0F80, 0x0F84),
            (0x0F86, 0x0F87),
            (0x0F8D, 0x0FBC),
        ],
        "blues": [],
    },
    {
        "name": "CJKV ideographs",
        "tag": "HANI",
        "hint_top_to_bottom": False,
        "std_chars": "田 囗", # 田 囗
        "base_ranges": [
            (0x1100, 0x11FF), # Hangul Jamo
            (0x2E80, 0x2EFF), # CJK Radicals Supplement
            (0x2F00, 0x2FDF), # Kangxi Radicals
            (0x2FF0, 0x2FFF), # Ideographic Description Characters
            (0x3000, 0x303F), # CJK Symbols and Punctuation
            (0x3040, 0x309F), # Hiragana
            (0x30A0, 0x30FF), # Katakana
            (0x3100, 0x312F), # Bopomofo
            (0x3130, 0x318F), # Hangul Compatibility Jamo
            (0x3190, 0x319F), # Kanbun
            (0x31A0, 0x31BF), # Bopomofo Extended
            (0x31C0, 0x31EF), # CJK Strokes
            (0x31F0, 0x31FF), # Katakana Phonetic Extensions
            (0x3300, 0x33FF), # CJK Compatibility
            (0x3400, 0x4DBF), # CJK Unified Ideographs Extension A
            (0x4DC0, 0x4DFF), # Yijing Hexagram Symbols
            (0x4E00, 0x9FFF), # CJK Unified Ideographs
            (0xA960, 0xA97F), # Hangul Jamo Extended-A
            (0xAC00, 0xD7AF), # Hangul Syllables
            (0xD7B0, 0xD7FF), # Hangul Jamo Extended-B
            (0xF900, 0xFAFF), # CJK Compatibility Ideographs
            (0xFE10, 0xFE1F), # Vertical forms
            (0xFE30, 0xFE4F), # CJK Compatibility Forms
            (0xFF00, 0xFFEF), # Halfwidth and Fullwidth Forms
            (0x1B000, 0x1B0FF), # Kana Supplement
            (0x1B100, 0x1B12F), # Kana Extended-A
            (0x1D300, 0x1D35F), # Tai Xuan Hing Symbols
            (0x20000, 0x2A6DF), # CJK Unified Ideographs Extension B
            (0x2A700, 0x2B73F), # CJK Unified Ideographs Extension C
            (0x2B740, 0x2B81F), # CJK Unified Ideographs Extension D
            (0x2B820, 0x2CEAF), # CJK Unified Ideographs Extension E
            (0x2CEB0, 0x2EBEF), # CJK Unified Ideographs Extension F
            (0x2F800, 0x2FA1F), # CJK Compatibility Ideographs Supplement
        ],
        "non_base_ranges": [
            (0x302A, 0x302F),
            (0x3190, 0x319F),
        ],
        "blues": [
            ("他 们 你 來 們 到 和 地 对 對 就 席 我 时 時 會 来 為 能 舰 說 说 这 這 齊 | 军 同 已 愿 既 星 是 景 民 照 现 現 理 用 置 要 軍 那 配 里 開 雷 露 面 顾", "TOP"),
            ("个 为 人 他 以 们 你 來 個 們 到 和 大 对 對 就 我 时 時 有 来 為 要 說 说 | 主 些 因 它 想 意 理 生 當 看 着 置 者 自 著 裡 过 还 进 進 過 道 還 里 面", "0"),
            (" 些 们 你 來 們 到 和 地 她 将 將 就 年 得 情 最 样 樣 理 能 說 说 这 這 通 | 即 吗 吧 听 呢 品 响 嗎 师 師 收 断 斷 明 眼 間 间 际 陈 限 除 陳 随 際 隨", "HORIZONTAL"),
            ("事 前 學 将 將 情 想 或 政 斯 新 样 樣 民 沒 没 然 特 现 現 球 第 經 谁 起 | 例 別 别 制 动 動 吗 嗎 增 指 明 朝 期 构 物 确 种 調 调 費 费 那 都 間 间", "HORIZONTAL | RIGHT"),
        ],
    },
]

CJK_GROUP = ["HANI"]
INDIC_GROUP = ["LIMB", "ORYA", "SYLO", "TIBT"]

def generate() -> str:
    buf = ""
    buf += "// THIS FILE IS AUTOGENERATED.\n"
    buf += "// Any changes to this file will be overwritten.\n"
    buf += "// Use ../scripts/gen_autohint_scripts.py to regenerate.\n\n"

    char_map = {}

    buf += "#[rustfmt::skip]\n"
    buf += "pub(super) const SCRIPT_CLASSES: &[ScriptClass] = &[\n"
    # some scripts generate multiple styles so keep track of the style index
    style_index = 0
    for i, script in enumerate(SCRIPT_CLASSES):
        std_chars = script["std_chars"]
        blues = script["blues"]
        tag = script["tag"]
        group = "Default"
        if tag in CJK_GROUP:
            group = "Cjk"
        elif tag in INDIC_GROUP:
            group = "Indic"
        unicode_tag = tag.lower().capitalize()
        has_features = tag in SCRIPTS_WITH_FEATURES
        buf += "    ScriptClass {\n"
        buf += "        name: \"{}\",\n".format(script["name"])
        buf += "        group: ScriptGroup::{},\n".format(group)
        buf += "        tag: Tag::new(b\"{}\"),\n".format(unicode_tag)
        buf += "        hint_top_to_bottom: {},\n".format(str(script["hint_top_to_bottom"]).lower())
        # standard characters
        buf += "        std_chars: \"{}\",\n".format(script["std_chars"])
        # blue characters
        buf += "        blues: &["
        if len(blues) != 0:
            buf += "\n";
            for blue in blues:
                blue_zones = "BlueZones::NONE"
                if blue[1] != "0":
                    zones = list("BlueZones::" + zone for zone in blue[1].split(" | "))
                    blue_zones = zones[0];
                    for flag in zones[1:]:
                        blue_zones += ".union(" + flag + ")"
                buf += "            (\"" + blue[0] + "\""
                buf += ", {}),\n".format(blue_zones)
            buf += "        ],\n"
        else:
            buf += "],\n"
        buf += "    },\n"
        if has_features:
            style_index += len(STYLE_FEATURES)
        bases = set()
        # build a char -> (script_ix, is_non_base) map for all ranges
        for char_range in script["base_ranges"]:
            first = char_range[0]
            last = char_range[1]
            # inclusive range
            for ch in range(first, last + 1):
                # Note: FT has overlapping ranges but we choose to keep
                # the first one to match behavior
                if not ch in char_map:
                    char_map[ch] = (style_index, False)
                    bases.add(ch)
        for char_range in script["non_base_ranges"]:
            first = char_range[0]
            last = char_range[1]
            # inclusive range
            for ch in range(first, last + 1):
                if ch in bases:
                    char_map[ch] = (style_index, True) # True for non-base character
        style_index += 1
    buf += "];\n\n"

    # Add some symbolic indices for each script so they can be
    # referenced by ScriptClass::LATN for example
    buf += "#[allow(unused)]"
    buf += "impl ScriptClass {\n"
    for i, script in enumerate(SCRIPT_CLASSES):
        buf += "    pub const {}: usize = {};\n".format(script["tag"], i)
    buf += "}\n\n"

    # Now run through scripts again and generate style classes
    buf += "#[rustfmt::skip]\n"
    buf += "pub(super) const STYLE_CLASSES: &[StyleClass] = &[\n"
    style_class_tags = []
    style_index = 0
    for i, script in enumerate(SCRIPT_CLASSES):
        tag = script["tag"]
        has_features = tag in SCRIPTS_WITH_FEATURES
        if has_features:
            for feature in STYLE_FEATURES:
                name = script["name"] + " " + feature["name"]
                feature_tag = feature["tag"]
                buf += "    StyleClass {{ name: \"{}\", index: {}, script: &SCRIPT_CLASSES[{}], feature: Some(Tag::new(b\"{}\")) }},\n".format(name, style_index, i, feature_tag)
                style_index += 1
                style_class_tags.append(tag + "_" + feature_tag.upper())
        name = script["name"]
        buf += "    StyleClass {{ name: \"{}\", index: {}, script: &SCRIPT_CLASSES[{}], feature: None }},\n".format(name, style_index, i)
        style_index += 1
        style_class_tags.append(tag)
    buf += "];\n\n";

    # Symbolic indices for style classes
    buf += "#[allow(unused)]"
    buf += "impl StyleClass {\n"
    for (i, tag) in enumerate(style_class_tags):
        buf += "    pub const {}: usize = {};\n".format(tag, i)
    buf += "}\n\n"    

    # build a sorted list from the map
    char_list = []
    for ch in char_map:
        char_list.append((ch, char_map[ch]))
    char_list.sort(key=lambda entry: entry[0])

    # and merge into ranges
    ranges = []
    for entry in char_list:
        ch = entry[0]
        props = entry[1]
        if len(ranges) != 0:
            last = ranges[-1];
            # we can merge if same props and this character extends the range
            # by 1
            if ch == last[1] + 1 and last[2] == props:
                ranges[-1] = (last[0], ch, props)
                continue
        ranges.append((ch, ch, props))

    # and finally output the ranges
    buf += "#[rustfmt::skip]\n"
    buf += "pub(super) const STYLE_RANGES: &[StyleRange] = &[\n"
    for char_range in ranges:
        first = char_range[0]
        last = char_range[1]
        props = char_range[2]
        kind = "base_range"
        if props[1]:
            kind = "non_base_range"
        buf += "    {}({}, {}, {}),\n".format(kind, first, last, props[0])
    buf += "];\n\n"
    return buf

if __name__ == "__main__":
    data = generate()
    with open("../generated/generated_autohint_styles.rs", "w", encoding="utf-8") as f:
        f.write(data)
