From 76df659ca023b29689ee58a4c43953ea650de524 Mon Sep 17 00:00:00 2001 From: Andrew Brampton Date: Sun, 21 Jan 2018 08:34:17 -0800 Subject: [PATCH 1/7] Added mapping of scripts, languages, and features tags to names. --- sfnt/names.go | 914 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 914 insertions(+) create mode 100644 sfnt/names.go diff --git a/sfnt/names.go b/sfnt/names.go new file mode 100644 index 0000000..772ebaf --- /dev/null +++ b/sfnt/names.go @@ -0,0 +1,914 @@ +package sfnt + +var ( + // languageTags contains the registered language names mapped by tag. + // See https://www.microsoft.com/typography/otspec/languagetags.htm + languageTags = map[string]string{ + "ABA ": "Abaza", + "ABK ": "Abkhazian", + "ACH ": "Acholi", + "ACR ": "Achi", + "ADY ": "Adyghe", + "AFK ": "Afrikaans", + "AFR ": "Afar", + "AGW ": "Agaw", + "AIO ": "Aiton", + "AKA ": "Akan", + "ALS ": "Alsatian", + "ALT ": "Altai", + "AMH ": "Amharic", + "ANG ": "Anglo-Saxon", + "APPH": "Phonetic transcription—Americanist conventions", + "ARA ": "Arabic", + "ARG ": "Aragonese", + "ARI ": "Aari", + "ARK ": "Rakhine", + "ASM ": "Assamese", + "AST ": "Asturian", + "ATH ": "Athapaskan", + "AVR ": "Avar", + "AWA ": "Awadhi", + "AYM ": "Aymara", + "AZB ": "Torki", + "AZE ": "Azerbaijani", + "BAD ": "Badaga", + "BAD0": "Banda", + "BAG ": "Baghelkhandi", + "BAL ": "Balkar", + "BAN ": "Balinese", + "BAR ": "Bavarian", + "BAU ": "Baulé", + "BBC ": "Batak Toba", + "BBR ": "Berber", + "BCH ": "Bench", + "BCR ": "Bible Cree", + "BDY ": "Bandjalang", + "BEL ": "Belarussian", + "BEM ": "Bemba", + "BEN ": "Bengali", + "BGC ": "Haryanvi", + "BGQ ": "Bagri", + "BGR ": "Bulgarian", + "BHI ": "Bhili", + "BHO ": "Bhojpuri", + "BIK ": "Bikol", + "BIL ": "Bilen", + "BIS ": "Bislama", + "BJJ ": "Kanauji", + "BKF ": "Blackfoot", + "BLI ": "Baluchi", + "BLK ": "Pa'o Karen", + "BLN ": "Balante", + "BLT ": "Balti", + "BMB ": "Bambara (Bamanankan)", + "BML ": "Bamileke", + "BOS ": "Bosnian", + "BPY ": "Bishnupriya Manipuri", + "BRE ": "Breton", + "BRH ": "Brahui", + "BRI ": "Braj Bhasha", + "BRM ": "Burmese", + "BRX ": "Bodo", + "BSH ": "Bashkir", + "BSK ": "Burushaski", + "BTI ": "Beti", + "BTS ": "Batak Simalungun", + "BUG ": "Bugis", + "BYV ": "Medumba", + "CAK ": "Kaqchikel", + "CAT ": "Catalan", + "CBK ": "Zamboanga Chavacano", + "CCHN": "Chinantec", + "CEB ": "Cebuano", + "CHE ": "Chechen", + "CHG ": "Chaha Gurage", + "CHH ": "Chattisgarhi", + "CHI ": "Chichewa (Chewa, Nyanja)", + "CHK ": "Chukchi", + "CHK0": "Chuukese", + "CHO ": "Choctaw", + "CHP ": "Chipewyan", + "CHR ": "Cherokee", + "CHA ": "Chamorro", + "CHU ": "Chuvash", + "CHY ": "Cheyenne", + "CGG ": "Chiga", + "CJA ": "Western Cham", + "CJM ": "Eastern Cham", + "CMR ": "Comorian", + "COP ": "Coptic", + "COR ": "Cornish", + "COS ": "Corsican", + "CPP ": "Creoles", + "CRE ": "Cree", + "CRR ": "Carrier", + "CRT ": "Crimean Tatar", + "CSB ": "Kashubian", + "CSL ": "Church Slavonic", + "CSY ": "Czech", + "CTG ": "Chittagonian", + "CUK ": "San Blas Kuna", + "DAN ": "Danish", + "DAR ": "Dargwa", + "DAX ": "Dayi", + "DCR ": "Woods Cree", + "DEU ": "German", + "DGO ": "Dogri", + "DGR ": "Dogri", + "DHG ": "Dhangu", + "DHV ": "E! Divehi (Dhivehi, Maldivian) (deprecated)", + "DIQ ": "Dimli", + "DIV ": "Divehi (Dhivehi, Maldivian)", + "DJR ": "Zarma", + "DJR0": "Djambarrpuyngu", + "DNG ": "Dangme", + "DNJ ": "Dan", + "DNK ": "Dinka", + "DRI ": "Dari", + "DUJ ": "Dhuwal", + "DUN ": "Dungan", + "DZN ": "Dzongkha", + "EBI ": "Ebira", + "ECR ": "Eastern Cree", + "EDO ": "Edo", + "EFI ": "Efik", + "ELL ": "Greek", + "EMK ": "Eastern Maninkakan", + "ENG ": "English", + "ERZ ": "Erzya", + "ESP ": "Spanish", + "ESU ": "Central Yupik", + "ETI ": "Estonian", + "EUQ ": "Basque", + "EVK ": "Evenki", + "EVN ": "Even", + "EWE ": "Ewe", + "FAN ": "French Antillean", + "FAN0": "Fang", + "FAR ": "Persian", + "FAT ": "Fanti", + "FIN ": "Finnish", + "FJI ": "Fijian", + "FLE ": "Dutch (Flemish)", + "FMP ": "Fe'fe'", + "FNE ": "Forest Nenets", + "FON ": "Fon", + "FOS ": "Faroese", + "FRA ": "French", + "FRC ": "Cajun French", + "FRI ": "Frisian", + "FRL ": "Friulian", + "FRP ": "Arpitan", + "FTA ": "Futa", + "FUL ": "Fulah", + "FUV ": "Nigerian Fulfulde", + "GAD ": "Ga", + "GAE ": "Scottish Gaelic (Gaelic)", + "GAG ": "Gagauz", + "GAL ": "Galician", + "GAR ": "Garshuni", + "GAW ": "Garhwali", + "GEZ ": "Ge'ez", + "GIH ": "Githabul", + "GIL ": "Gilyak", + "GIL0": "Kiribati (Gilbertese)", + "GKP ": "Kpelle (Guinea)", + "GLK ": "Gilaki", + "GMZ ": "Gumuz", + "GNN ": "Gumatj", + "GOG ": "Gogo", + "GON ": "Gondi", + "GRN ": "Greenlandic", + "GRO ": "Garo", + "GUA ": "Guarani", + "GUC ": "Wayuu", + "GUF ": "Gupapuyngu", + "GUJ ": "Gujarati", + "GUZ ": "Gusii", + "HAI ": "Haitian (Haitian Creole)", + "HAL ": "Halam", + "HAR ": "Harauti", + "HAU ": "Hausa", + "HAW ": "Hawaiian", + "HAY ": "Haya", + "HAZ ": "Hazaragi", + "HBN ": "Hammer-Banna", + "HER ": "Herero", + "HIL ": "Hiligaynon", + "HIN ": "Hindi", + "HMA ": "High Mari", + "HMN ": "Hmong", + "HMO ": "Hiri Motu", + "HND ": "Hindko", + "HO ": "Ho", + "HRI ": "Harari", + "HRV ": "Croatian", + "HUN ": "Hungarian", + "HYE ": "Armenian", + "HYE0": "Armenian East", + "IBA ": "Iban", + "IBB ": "Ibibio", + "IBO ": "Igbo", + "IJO ": "Ijo languages", + "IDO ": "Ido", + "ILE ": "Interlingue", + "ILO ": "Ilokano", + "INA ": "Interlingua", + "IND ": "Indonesian", + "ING ": "Ingush", + "INU ": "Inuktitut", + "IPK ": "Inupiat", + "IPPH": "Phonetic transcription—IPA conventions", + "IRI ": "Irish", + "IRT ": "Irish Traditional", + "ISL ": "Icelandic", + "ISM ": "Inari Sami", + "ITA ": "Italian", + "IWR ": "Hebrew", + "JAM ": "Jamaican Creole", + "JAN ": "Japanese", + "JAV ": "Javanese", + "JBO ": "Lojban", + "JCT ": "Krymchak", + "JII ": "Yiddish", + "JUD ": "Ladino", + "JUL ": "Jula", + "KAB ": "Kabardian", + "KAB0": "Kabyle", + "KAC ": "Kachchi", + "KAL ": "Kalenjin", + "KAN ": "Kannada", + "KAR ": "Karachay", + "KAT ": "Georgian", + "KAZ ": "Kazakh", + "KDE ": "Makonde", + "KEA ": "Kabuverdianu (Crioulo)", + "KEB ": "Kebena", + "KEK ": "Kekchi", + "KGE ": "Khutsuri Georgian", + "KHA ": "Khakass", + "KHK ": "Khanty-Kazim", + "KHM ": "Khmer", + "KHS ": "Khanty-Shurishkar", + "KHT ": "Khamti Shan", + "KHV ": "Khanty-Vakhi", + "KHW ": "Khowar", + "KIK ": "Kikuyu (Gikuyu)", + "KIR ": "Kirghiz (Kyrgyz)", + "KIS ": "Kisii", + "KIU ": "Kirmanjki", + "KJD ": "Southern Kiwai", + "KJP ": "Eastern Pwo Karen", + "KKN ": "Kokni", + "KLM ": "Kalmyk", + "KMB ": "Kamba", + "KMN ": "Kumaoni", + "KMO ": "Komo", + "KMS ": "Komso", + "KMZ ": "Khorasani Turkic", + "KNR ": "Kanuri", + "KOD ": "Kodagu", + "KOH ": "Korean Old Hangul", + "KOK ": "Konkani", + "KON ": "Kikongo", + "KOM ": "Komi", + "KON0": "Kongo", + "KOP ": "Komi-Permyak", + "KOR ": "Korean", + "KOS ": "Kosraean", + "KOZ ": "Komi-Zyrian", + "KPL ": "Kpelle", + "KRI ": "Krio", + "KRK ": "Karakalpak", + "KRL ": "Karelian", + "KRM ": "Karaim", + "KRN ": "Karen", + "KRT ": "Koorete", + "KSH ": "Kashmiri", + "KSH0": "Ripuarian", + "KSI ": "Khasi", + "KSM ": "Kildin Sami", + "KSW ": "S’gaw Karen", + "KUA ": "Kuanyama", + "KUI ": "Kui", + "KUL ": "Kulvi", + "KUM ": "Kumyk", + "KUR ": "Kurdish", + "KUU ": "Kurukh", + "KUY ": "Kuy", + "KYK ": "Koryak", + "KYU ": "Western Kayah", + "LAD ": "Ladin", + "LAH ": "Lahuli", + "LAK ": "Lak", + "LAM ": "Lambani", + "LAO ": "Lao", + "LAT ": "Latin", + "LAZ ": "Laz", + "LCR ": "L-Cree", + "LDK ": "Ladakhi", + "LEZ ": "Lezgi", + "LIJ ": "Ligurian", + "LIM ": "Limburgish", + "LIN ": "Lingala", + "LIS ": "Lisu", + "LJP ": "Lampung", + "LKI ": "Laki", + "LMA ": "Low Mari", + "LMB ": "Limbu", + "LMO ": "Lombard", + "LMW ": "Lomwe", + "LOM ": "Loma", + "LRC ": "Luri", + "LSB ": "Lower Sorbian", + "LSM ": "Lule Sami", + "LTH ": "Lithuanian", + "LTZ ": "Luxembourgish", + "LUA ": "Luba-Lulua", + "LUB ": "Luba-Katanga", + "LUG ": "Ganda", + "LUH ": "Luyia", + "LUO ": "Luo", + "LVI ": "Latvian", + "MAD ": "Madura", + "MAG ": "Magahi", + "MAH ": "Marshallese", + "MAJ ": "Majang", + "MAK ": "Makhuwa", + "MAL ": "Malayalam", + "MAM ": "Mam", + "MAN ": "Mansi", + "MAP ": "Mapudungun", + "MAR ": "Marathi", + "MAW ": "Marwari", + "MBN ": "Mbundu", + "MBO ": "Mbo", + "MCH ": "Manchu", + "MCR ": "Moose Cree", + "MDE ": "Mende", + "MDR ": "Mandar", + "MEN ": "Me'en", + "MER ": "Meru", + "MFE ": "Morisyen", + "MIN ": "Minangkabau", + "MIZ ": "Mizo", + "MKD ": "Macedonian", + "MKR ": "Makasar", + "MKW ": "Kituba", + "MLE ": "Male", + "MLG ": "Malagasy", + "MLN ": "Malinke", + "MLR ": "Malayalam Reformed", + "MLY ": "Malay", + "MND ": "Mandinka", + "MNG ": "Mongolian", + "MNI ": "Manipuri", + "MNK ": "Maninka", + "MNX ": "Manx", + "MOH ": "Mohawk", + "MOK ": "Moksha", + "MOL ": "Moldavian", + "MON ": "Mon", + "MOR ": "Moroccan", + "MOS ": "Mossi", + "MRI ": "Maori", + "MTH ": "Maithili", + "MTS ": "Maltese", + "MUN ": "Mundari", + "MUS ": "Muscogee", + "MWL ": "Mirandese", + "MWW ": "Hmong Daw", + "MYN ": "Mayan", + "MZN ": "Mazanderani", + "NAG ": "Naga-Assamese", + "NAH ": "Nahuatl", + "NAN ": "Nanai", + "NAP ": "Neapolitan", + "NAS ": "Naskapi", + "NAU ": "Nauruan", + "NAV ": "Navajo", + "NCR ": "N-Cree", + "NDB ": "Ndebele", + "NDC ": "Ndau", + "NDG ": "Ndonga", + "NDS ": "Low Saxon", + "NEP ": "Nepali", + "NEW ": "Newari", + "NGA ": "Ngbaka", + "NGR ": "Nagari", + "NHC ": "Norway House Cree", + "NIS ": "Nisi", + "NIU ": "Niuean", + "NKL ": "Nyankole", + "NKO ": "N'Ko", + "NLD ": "Dutch", + "NOE ": "Nimadi", + "NOG ": "Nogai", + "NOR ": "Norwegian", + "NOV ": "Novial", + "NSM ": "Northern Sami", + "NSO ": "Sotho, Northern", + "NTA ": "Northern Tai", + "NTO ": "Esperanto", + "NYM ": "Nyamwezi", + "NYN ": "Norwegian Nynorsk (Nynorsk, Norwegian)", + "NZA ": "Mbembe Tigon", + "OCI ": "Occitan", + "OCR ": "Oji-Cree", + "OJB ": "Ojibway", + "ORI ": "Odia (formerly Oriya)", + "ORO ": "Oromo", + "OSS ": "Ossetian", + "PAA ": "Palestinian Aramaic", + "PAG ": "Pangasinan", + "PAL ": "Pali", + "PAM ": "Pampangan", + "PAN ": "Punjabi", + "PAP ": "Palpa", + "PAP0": "Papiamentu", + "PAS ": "Pashto", + "PAU ": "Palauan", + "PCC ": "Bouyei", + "PCD ": "Picard", + "PDC ": "Pennsylvania German", + "PGR ": "Polytonic Greek", + "PHK ": "Phake", + "PIH ": "Norfolk", + "PIL ": "Filipino", + "PLG ": "Palaung", + "PLK ": "Polish", + "PMS ": "Piemontese", + "PNB ": "Western Panjabi", + "POH ": "Pocomchi", + "PON ": "Pohnpeian", + "PRO ": "Provencal", + "PTG ": "Portuguese", + "PWO ": "Western Pwo Karen", + "QIN ": "Chin", + "QUC ": "K’iche’", + "QUH ": "Quechua (Bolivia)", + "QUZ ": "Quechua", + "QVI ": "Quechua (Ecuador)", + "QWH ": "Quechua (Peru)", + "RAJ ": "Rajasthani", + "RAR ": "Rarotongan", + "RBU ": "Russian Buriat", + "RCR ": "R-Cree", + "REJ ": "Rejang", + "RIA ": "Riang", + "RIF ": "Tarifit", + "RIT ": "Ritarungo", + "RKW ": "Arakwal", + "RMS ": "Romansh", + "RMY ": "Vlax Romani", + "ROM ": "Romanian", + "ROY ": "Romany", + "RSY ": "Rusyn", + "RTM ": "Rotuman", + "RUA ": "Kinyarwanda", + "RUN ": "Rundi", + "RUP ": "Aromanian", + "RUS ": "Russian", + "SAD ": "Sadri", + "SAN ": "Sanskrit", + "SAS ": "Sasak", + "SAT ": "Santali", + "SAY ": "Sayisi", + "SCN ": "Sicilian", + "SCO ": "Scots", + "SEK ": "Sekota", + "SEL ": "Selkup", + "SGA ": "Old Irish", + "SGO ": "Sango", + "SGS ": "Samogitian", + "SHI ": "Tachelhit", + "SHN ": "Shan", + "SIB ": "Sibe", + "SID ": "Sidamo", + "SIG ": "Silte Gurage", + "SKS ": "Skolt Sami", + "SKY ": "Slovak", + "SCS ": "North Slavey", + "SLA ": "Slavey", + "SLV ": "Slovenian", + "SML ": "Somali", + "SMO ": "Samoan", + "SNA ": "Sena", + "SNA0": "Shona", + "SND ": "Sindhi", + "SNH ": "Sinhala (Sinhalese)", + "SNK ": "Soninke", + "SOG ": "Sodo Gurage", + "SOP ": "Songe", + "SOT ": "Sotho, Southern", + "SQI ": "Albanian", + "SRB ": "Serbian", + "SRD ": "Sardinian", + "SRK ": "Saraiki", + "SRR ": "Serer", + "SSL ": "South Slavey", + "SSM ": "Southern Sami", + "STQ ": "Saterland Frisian", + "SUK ": "Sukuma", + "SUN ": "Sundanese", + "SUR ": "Suri", + "SVA ": "Svan", + "SVE ": "Swedish", + "SWA ": "Swadaya Aramaic", + "SWK ": "Swahili", + "SWZ ": "Swati", + "SXT ": "Sutu", + "SXU ": "Upper Saxon", + "SYL ": "Sylheti", + "SYR ": "Syriac", + "SYRE": "Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre')", + "SYRJ": "Syriac, Western script-variant (equivalent to ISO 15924 'Syrj')", + "SYRN": "Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn')", + "SZL ": "Silesian", + "TAB ": "Tabasaran", + "TAJ ": "Tajiki", + "TAM ": "Tamil", + "TAT ": "Tatar", + "TCR ": "TH-Cree", + "TDD ": "Dehong Dai", + "TEL ": "Telugu", + "TET ": "Tetum", + "TGL ": "Tagalog", + "TGN ": "Tongan", + "TGR ": "Tigre", + "TGY ": "Tigrinya", + "THA ": "Thai", + "THT ": "Tahitian", + "TIB ": "Tibetan", + "TIV ": "Tiv", + "TKM ": "Turkmen", + "TMH ": "Tamashek", + "TMN ": "Temne", + "TNA ": "Tswana", + "TNE ": "Tundra Nenets", + "TNG ": "Tonga", + "TOD ": "Todo", + "TOD0": "Toma", + "TPI ": "Tok Pisin", + "TRK ": "Turkish", + "TSG ": "Tsonga", + "TUA ": "Turoyo Aramaic", + "TUM ": "Tulu", + "TUL ": "Tumbuka", + "TUV ": "Tuvin", + "TVL ": "Tuvalu", + "TWI ": "Twi", + "TYZ ": "Tày", + "TZM ": "Tamazight", + "TZO ": "Tzotzil", + "UDM ": "Udmurt", + "UKR ": "Ukrainian", + "UMB ": "Umbundu", + "URD ": "Urdu", + "USB ": "Upper Sorbian", + "UYG ": "Uyghur", + "UZB ": "Uzbek", + "VEC ": "Venetian", + "VEN ": "Venda", + "VIT ": "Vietnamese", + "VOL ": "Volapük", + "VRO ": "Võro", + "WA ": "Wa", + "WAG ": "Wagdi", + "WAR ": "Waray-Waray", + "WCR ": "West-Cree", + "WEL ": "Welsh", + "WLN ": "Walloon", + "WLF ": "Wolof", + "WTM ": "Mewati", + "XBD ": "Lü", + "XHS ": "Xhosa", + "XJB ": "Minjangbal", + "XOG ": "Soga", + "XPE ": "Kpelle (Liberia)", + "YAK ": "Sakha", + "YAO ": "Yao", + "YAP ": "Yapese", + "YBA ": "Yoruba", + "YCR ": "Y-Cree", + "YIC ": "Yi Classic", + "YIM ": "Yi Modern", + "ZEA ": "Zealandic", + "ZGH ": "Standard Morrocan Tamazigh", + "ZHA ": "Zhuang", + "ZHH ": "Chinese, Hong Kong SAR", + "ZHP ": "Chinese Phonetic", + "ZHS ": "Chinese Simplified", + "ZHT ": "Chinese Traditional", + "ZND ": "Zande", + "ZUL ": "Zulu", + "ZZA ": "Zazaki", + } + + // scriptTags contains the registered script names mapped by tag. + // See https://www.microsoft.com/typography/otspec/scripttags.htm + scriptTags = map[string]string{ + "adlm": "Adlam", + "ahom": "Ahom", + "hluw": "Anatolian Hieroglyphs", + "arab": "Arabic", + "armn": "Armenian", + "avst": "Avestan", + "bali": "Balinese", + "bamu": "Bamum", + "bass": "Bassa Vah", + "batk": "Batak", + "beng": "Bengali", + "bng2": "Bengali v.2", + "bhks": "Bhaiksuki", + "bopo": "Bopomofo", + "brah": "Brahmi", + "brai": "Braille", + "bugi": "Buginese", + "buhd": "Buhid", + "byzm": "Byzantine Music", + "cans": "Canadian Syllabics", + "cari": "Carian", + "aghb": "Caucasian Albanian", + "cakm": "Chakma", + "cham": "Cham", + "cher": "Cherokee", + "hani": "CJK Ideographic", + "copt": "Coptic", + "cprt": "Cypriot Syllabary", + "cyrl": "Cyrillic", + "DFLT": "Default", + "dsrt": "Deseret", + "deva": "Devanagari", + "dev2": "Devanagari v.2", + "dupl": "Duployan", + "egyp": "Egyptian Hieroglyphs", + "elba": "Elbasan", + "ethi": "Ethiopic", + "geor": "Georgian", + "glag": "Glagolitic", + "goth": "Gothic", + "gran": "Grantha", + "grek": "Greek", + "gujr": "Gujarati", + "gjr2": "Gujarati v.2", + "guru": "Gurmukhi", + "gur2": "Gurmukhi v.2", + "hang": "Hangul", + "jamo": "Hangul Jamo", + "hano": "Hanunoo", + "hatr": "Hatran", + "hebr": "Hebrew", + "kana": "Hiragana + Katakana", + "armi": "Imperial Aramaic", + "phli": "Inscriptional Pahlavi", + "prti": "Inscriptional Parthian", + "java": "Javanese", + "kthi": "Kaithi", + "knda": "Kannada", + "knd2": "Kannada v.2", + "kali": "Kayah Li", + "khar": "Kharosthi", + "khmr": "Khmer", + "khoj": "Khojki", + "sind": "Khudawadi", + "lao ": "Lao", + "latn": "Latin", + "lepc": "Lepcha", + "limb": "Limbu", + "lina": "Linear A", + "linb": "Linear B", + "lisu": "Lisu (Fraser)", + "lyci": "Lycian", + "lydi": "Lydian", + "mahj": "Mahajani", + "mlym": "Malayalam", + "mlm2": "Malayalam v.2", + "mand": "Mandaic, Mandaean", + "mani": "Manichaean", + "marc": "Marchen", + "math": "Mathematical Alphanumeric Symbols", + "mtei": "Meitei Mayek (Meithei, Meetei)", + "mend": "Mende Kikakui", + "merc": "Meroitic Cursive", + "mero": "Meroitic Hieroglyphs", + "plrd": "Miao", + "modi": "Modi", + "mong": "Mongolian", + "mroo": "Mro", + "mult": "Multani", + "musc": "Musical Symbols", + "mymr": "Myanmar", + "mym2": "Myanmar v.2", + "nbat": "Nabataean", + "newa": "Newa", + "talu": "New Tai Lue", + "nko ": "N'Ko", + "orya": "Odia (formerly Oriya)", + "ory2": "Odia v.2 (formerly Oriya v.2)", + "ogam": "Ogham", + "olck": "Ol Chiki", + "ital": "Old Italic", + "hung": "Old Hungarian", + "narb": "Old North Arabian", + "perm": "Old Permic", + "xpeo": "Old Persian Cuneiform", + "sarb": "Old South Arabian", + "orkh": "Old Turkic, Orkhon Runic", + "osge": "Osage", + "osma": "Osmanya", + "hmng": "Pahawh Hmong", + "palm": "Palmyrene", + "pauc": "Pau Cin Hau", + "phag": "Phags-pa", + "phnx": "Phoenician", + "phlp": "Psalter Pahlavi", + "rjng": "Rejang", + "runr": "Runic", + "samr": "Samaritan", + "saur": "Saurashtra", + "shrd": "Sharada", + "shaw": "Shavian", + "sidd": "Siddham", + "sgnw": "Sign Writing", + "sinh": "Sinhala", + "sora": "Sora Sompeng", + "xsux": "Sumero-Akkadian Cuneiform", + "sund": "Sundanese", + "sylo": "Syloti Nagri", + "syrc": "Syriac", + "tglg": "Tagalog", + "tagb": "Tagbanwa", + "tale": "Tai Le", + "lana": "Tai Tham (Lanna)", + "tavt": "Tai Viet", + "takr": "Takri", + "taml": "Tamil", + "tml2": "Tamil v.2", + "tang": "Tangut", + "telu": "Telugu", + "tel2": "Telugu v.2", + "thaa": "Thaana", + "thai": "Thai", + "tibt": "Tibetan", + "tfng": "Tifinagh", + "tirh": "Tirhuta", + "ugar": "Ugaritic Cuneiform", + "vai ": "Vai", + "wara": "Warang Citi", + "yi ": "Yi", + } + + // featureTags contains the registered feature names mapped by tag. + // See https://www.microsoft.com/typography/otspec/featurelist.htm + featureTags = map[string]string{ + "aalt": "Access All Alternates", + "abvf": "Above-base Forms", + "abvm": "Above-base Mark Positioning", + "abvs": "Above-base Substitutions", + "afrc": "Alternative Fractions", + "akhn": "Akhands", + "blwf": "Below-base Forms", + "blwm": "Below-base Mark Positioning", + "blws": "Below-base Substitutions", + "c2pc": "Petite Capitals From Capitals", + "c2sc": "Small Capitals From Capitals", + "calt": "Contextual Alternates", + "case": "Case-Sensitive Forms", + "ccmp": "Glyph Composition / Decomposition", + "cfar": "Conjunct Form After Ro", + "cjct": "Conjunct Forms", + "clig": "Contextual Ligatures", + "cpct": "Centered CJK Punctuation", + "cpsp": "Capital Spacing", + "crcy": "Currency (Deprecated)", + "cswh": "Contextual Swash", + "curs": "Cursive Positioning", + // "cv01"..."cv99": "Character Variants", // Handled in Feature.String() + "dist": "Distances", + "dlig": "Discretionary Ligatures", + "dnom": "Denominators", + "dpng": "Diphthongs (Deprecated)", + "dtls": "Dotless Forms", + "expt": "Expert Forms", + "falt": "Final Glyph on Line Alternates", + "fin2": "Terminal Forms #2", + "fin3": "Terminal Forms #3", + "fina": "Terminal Forms", + "flac": "Flattened accent forms", + "frac": "Fractions", + "fwid": "Full Widths", + "half": "Half Forms", + "haln": "Halant Forms", + "halt": "Alternate Half Widths", + "hist": "Historical Forms", + "hkna": "Horizontal Kana Alternates", + "hlig": "Historical Ligatures", + "hngl": "Hangul", + "hojo": "Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms)", + "hwid": "Half Widths", + "init": "Initial Forms", + "isol": "Isolated Forms", + "ital": "Italics", + "jajp": "Japanese Forms (Deprecated)", + "jalt": "Justification Alternates", + "jp04": "JIS2004 Forms", + "jp78": "JIS78 Forms", + "jp83": "JIS83 Forms", + "jp90": "JIS90 Forms", + "kern": "Kerning", + "kokr": "Korean Forms (Deprecated)", + "lfbd": "Left Bounds", + "liga": "Standard Ligatures", + "ljmo": "Leading Jamo Forms", + "lnum": "Lining Figures", + "locl": "Localized Forms", + "ltra": "Left-to-right alternates", + "ltrm": "Left-to-right mirrored forms", + "mark": "Mark Positioning", + "med2": "Medial Forms #2", + "medi": "Medial Forms", + "mgrk": "Mathematical Greek", + "mkmk": "Mark to Mark Positioning", + "mset": "Mark Positioning via Substitution", + "nalt": "Alternate Annotation Forms", + "nlck": "NLC Kanji Forms", + "nukt": "Nukta Forms", + "numr": "Numerators", + "onum": "Oldstyle Figures", + "opbd": "Optical Bounds", + "ordn": "Ordinals", + "ornm": "Ornaments", + "palt": "Proportional Alternate Widths", + "pcap": "Petite Capitals", + "pkna": "Proportional Kana", + "pnum": "Proportional Figures", + "pref": "Pre-Base Forms", + "pres": "Pre-base Substitutions", + "pstf": "Post-base Forms", + "psts": "Post-base Substitutions", + "pwid": "Proportional Widths", + "qwid": "Quarter Widths", + "rand": "Randomize", + "rclt": "Required Contextual Alternates", + "rkrf": "Rakar Forms", + "rlig": "Required Ligatures", + "rphf": "Reph Forms", + "rtbd": "Right Bounds", + "rtla": "Right-to-left alternates", + "rtlm": "Right-to-left mirrored forms", + "ruby": "Ruby Notation Forms", + "rvrn": "Required Variation Alternates", + "salt": "Stylistic Alternates", + "sinf": "Scientific Inferiors", + "size": "Optical size", + "smcp": "Small Capitals", + "smpl": "Simplified Forms", + "ss01": "Stylistic Set 1", + "ss02": "Stylistic Set 2", + "ss03": "Stylistic Set 3", + "ss04": "Stylistic Set 4", + "ss05": "Stylistic Set 5", + "ss06": "Stylistic Set 6", + "ss07": "Stylistic Set 7", + "ss08": "Stylistic Set 8", + "ss09": "Stylistic Set 9", + "ss10": "Stylistic Set 10", + "ss11": "Stylistic Set 11", + "ss12": "Stylistic Set 12", + "ss13": "Stylistic Set 13", + "ss14": "Stylistic Set 14", + "ss15": "Stylistic Set 15", + "ss16": "Stylistic Set 16", + "ss17": "Stylistic Set 17", + "ss18": "Stylistic Set 18", + "ss19": "Stylistic Set 19", + "ss20": "Stylistic Set 20", + "ssty": "Math script style alternates", + "stch": "Stretching Glyph Decomposition", + "subs": "Subscript", + "sups": "Superscript", + "swsh": "Swash", + "titl": "Titling", + "tjmo": "Trailing Jamo Forms", + "tnam": "Traditional Name Forms", + "tnum": "Tabular Figures", + "trad": "Traditional Forms", + "twid": "Third Widths", + "unic": "Unicase", + "valt": "Alternate Vertical Metrics", + "vatu": "Vattu Variants", + "vert": "Vertical Writing", + "vhal": "Alternate Vertical Half Metrics", + "vivn": "Vietnamese Forms (Deprecated)", + "vjmo": "Vowel Jamo Forms", + "vkna": "Vertical Kana Alternates", + "vkrn": "Vertical Kerning", + "vpal": "Proportional Alternate Vertical Metrics", + "vrt2": "Vertical Alternates and Rotation", + "vrtr": "Vertical Alternates for Rotation", + "zero": "Slashed Zero", + "zhcn": "Simplified Chinese Forms (Deprecated)", + "zhtw": "Traditional Chinese Forms (Deprecated)", + } +) From c7a3e4c5382ac8c1e42fd6bd3bc4847017ae10b0 Mon Sep 17 00:00:00 2001 From: Andrew Brampton Date: Sun, 21 Jan 2018 08:57:42 -0800 Subject: [PATCH 2/7] Add parsing of most of the GPOS/GSUB layout tables. --- sfnt/font.go | 12 ++ sfnt/table.go | 5 + sfnt/table_feature.go | 417 ++++++++++++++++++++++++++++++++++++++++++ sfnt/tag.go | 4 + 4 files changed, 438 insertions(+) create mode 100644 sfnt/table_feature.go diff --git a/sfnt/font.go b/sfnt/font.go index d8e3286..d8f755d 100644 --- a/sfnt/font.go +++ b/sfnt/font.go @@ -120,6 +120,18 @@ func (font *Font) OS2Table() *TableOS2 { return font.tables[TagOS2].(*TableOS2) } +// GposTable returns the Glyph Positioning table identified with the 'GPOS' tag. +// This method will panic if the font doesn't have this table. +func (font *Font) GposTable() *TableLayout { + return font.tables[TagGpos].(*TableLayout) +} + +// GsubTable returns the Glyph Substitution table identified with the 'GSUB' tag. +// This method will panic if the font doesn't have this table. +func (font *Font) GsubTable() *TableLayout { + return font.tables[TagGsub].(*TableLayout) +} + func (font *Font) Table(tag Tag) Table { return font.tables[tag] } diff --git a/sfnt/table.go b/sfnt/table.go index d2d1715..c1c47ed 100644 --- a/sfnt/table.go +++ b/sfnt/table.go @@ -31,6 +31,11 @@ func parseTable(tag Tag, buffer io.Reader) (Table, error) { return parseTableHhea(buffer) } else if tag == TagOS2 { return parseTableOS2(buffer) + } else if tag == TagGpos { + return parseTableLayout(buffer) + } else if tag == TagGsub { + return parseTableLayout(buffer) } + return newUnparsedTable(buffer) } diff --git a/sfnt/table_feature.go b/sfnt/table_feature.go new file mode 100644 index 0000000..f512507 --- /dev/null +++ b/sfnt/table_feature.go @@ -0,0 +1,417 @@ +package sfnt + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "io/ioutil" +) + +// TableLayout represents the common layout table used by GPOS and GSUB. +// The Features field contains all the features for this layout. However, +// the script and language determines which feature is used. +// +// See https://www.microsoft.com/typography/otspec/chapter2.htm#organization +// See https://www.microsoft.com/typography/otspec/GPOS.htm +// See https://www.microsoft.com/typography/otspec/GSUB.htm +type TableLayout struct { + bytes []byte + version versionHeader + header layoutHeader11 + + Scripts []*Script // Scripts contains all the scripts in this layout. + Features []*Feature // Features contains all the features in this layout. + Lookups []*Lookup // Lookups contains all the lookups in this layout. +} + +// Bytes returns the bytes for this table. The TableLayout is read only, so +// the bytes will always be the same as what is read in. +func (t *TableLayout) Bytes() []byte { + return t.bytes +} + +// Script represents a single script (i.e "latn" (Latin), "cyrl" (Cyrillic), etc). +type Script struct { + Tag Tag // Tag for this script. + DefaultLanguage *LangSys // DefaultLanguage used by this script. + Languages []*LangSys // Languages within this script. +} + +// Script returns the name for this script. +func (s *Script) String() string { + return scriptTags[s.Tag.String()] +} + +// LangSys represents the language system for a script. +type LangSys struct { + Tag Tag // Tag for this language. + Features []*Feature // Features contains the features for this language. +} + +// String returns the name for this language. +func (l *LangSys) String() string { + return languageTags[l.Tag.String()] +} + +// Feature represents a glyph substitution or glyph positioning features. +type Feature struct { + Tag Tag // Tag for this feature +} + +// Script returns the name for this feature. +func (f *Feature) String() string { + tag := f.Tag.String() + if name, found := featureTags[tag]; found { + return name + } + + if len(tag) == 4 && tag[0] == 'c' && tag[1] == 'v' && tag[2] >= '0' && tag[2] <= '9' && tag[3] >= '0' && tag[3] <= '9' { + return "Character Variants" + } + + return "" +} + +// Lookup represents a feature lookup table. +type Lookup struct { + Type uint16 // Different enumerations for GSUB and GPOS. + Flag uint16 // Lookup qualifiers. +} + +// versionHeader is the beginning of on-disk format of the GPOS/GSUB version header. +// See https://www.microsoft.com/typography/otspec/GPOS.htm +// See https://www.microsoft.com/typography/otspec/GSUB.htm +type versionHeader struct { + Major uint16 // Major version of the GPOS/GSUB table. + Minor uint16 // Minor version of the GPOS/GSUB table. +} + +// layoutHeader10 is the on-disk format of GPOS/GSUB version header when major=1 and minor=0. +type layoutHeader10 struct { + ScriptListOffset uint16 // offset to ScriptList table, from beginning of GPOS/GSUB table. + FeatureListOffset uint16 // offset to FeatureList table, from beginning of GPOS/GSUB table. + LookupListOffset uint16 // offset to LookupList table, from beginning of GPOS/GSUB table. +} + +// layoutHeader11 is the on-disk format of GPOS/GSUB version header when major=1 and minor=1. +type layoutHeader11 struct { + layoutHeader10 + FeatureVariationsOffset uint32 // offset to FeatureVariations table, from beginning of GPOS/GSUB table (may be NULL). +} + +// tagOffsetRecord is a on-disk format of a Tag and Offset record, commonly used thoughout this table. +type tagOffsetRecord struct { + Tag Tag // 4-byte script tag identifier + Offset uint16 // Offset to object from beginning of list +} +type scriptRecord tagOffsetRecord +type featureRecord tagOffsetRecord +type lookupRecord tagOffsetRecord +type langSysRecord tagOffsetRecord + +type scriptTable struct { + DefaultLangSys uint16 // Offset to default LangSys table, from beginning of Script table — may be NULL + LangSysCount uint16 // Number of LangSysRecords for this script — excluding the default LangSys + // langSysRecords[langSysCount] langSysRecord // Array of LangSysRecords, listed alphabetically by LangSys tag +} + +type featureTable struct { + FeatureParams uint16 // = NULL (reserved for offset to FeatureParams) + LookupIndexCount uint16 // Number of LookupList indices for this feature + // lookupListIndices [lookupIndexCount]uint16 // Array of indices into the LookupList — zero-based (first lookup is LookupListIndex = 0)} +} + +type lookupTable struct { + Type uint16 // Different enumerations for GSUB and GPOS + Flag uint16 // Lookup qualifiers + SubTableCount uint16 // Number of subtables for this lookup + // subtableOffsets[subTableCount] uint16 // Array of offsets to lookup subtables, from beginning of Lookup table + // markFilteringSet uint16 // Index (base 0) into GDEF mark glyph sets structure. This field is only present if bit useMarkFilteringSet of lookup flags is set. +} + +type langSysTable struct { + LookupOrder uint16 // = NULL (reserved for an offset to a reordering table) + RequiredFeatureIndex uint16 // Index of a feature required for this language system; if no required features = 0xFFFF + FeatureIndexCount uint16 // Number of feature index values for this language system — excludes the required feature + // featureIndices[featureIndexCount] uint16 // Array of indices into the FeatureList, in arbitrary order +} + +// parseLangSys parses a single Language System table. b expected to be the beginning of Script table. +// See https://www.microsoft.com/typography/otspec/chapter2.htm#langSysTbl +func (t *TableLayout) parseLangSys(b []byte, record langSysRecord) (*LangSys, error) { + if int(record.Offset) >= len(b) { + return nil, io.ErrUnexpectedEOF + } + + r := bytes.NewReader(b[record.Offset:]) + + var lang langSysTable + if err := binary.Read(r, binary.BigEndian, &lang); err != nil { + return nil, fmt.Errorf("reading langSysTable: %s", err) + } + + featureIndices := make([]uint16, lang.FeatureIndexCount, lang.FeatureIndexCount) + if err := binary.Read(r, binary.BigEndian, &featureIndices); err != nil { + return nil, fmt.Errorf("reading langSysTable featureIndices[%d]: %s", lang.FeatureIndexCount, err) + } + + var features []*Feature + for i := 0; i < len(featureIndices); i++ { + if int(featureIndices[i]) >= len(t.Features) { + return nil, fmt.Errorf("invalid featureIndices[%d] = %d", i, featureIndices[i]) + } + features = append(features, t.Features[featureIndices[i]]) + } + + return &LangSys{ + Tag: record.Tag, + Features: features, + }, nil +} + +// parseScript parses a single Script table. b expected to be the beginning of ScriptList. +// See https://www.microsoft.com/typography/otspec/chapter2.htm#sTbl_lsRec +func (t *TableLayout) parseScript(b []byte, record scriptRecord) (*Script, error) { + if int(record.Offset) >= len(b) { + return nil, io.ErrUnexpectedEOF + } + + b = b[record.Offset:] + r := bytes.NewReader(b) + + var script scriptTable + if err := binary.Read(r, binary.BigEndian, &script); err != nil { + return nil, fmt.Errorf("reading scriptTable: %s", err) + } + + var defaultLang *LangSys + var langs []*LangSys + + if script.DefaultLangSys > 0 { + var err error + defaultLang, err = t.parseLangSys(b, langSysRecord{Offset: script.DefaultLangSys}) + if err != nil { + return nil, err + } + } + + for i := 0; i < int(script.LangSysCount); i++ { + var record langSysRecord + if err := binary.Read(r, binary.BigEndian, &record); err != nil { + return nil, fmt.Errorf("reading langSysRecord[%d]: %s", i, err) + } + + if record.Offset == script.DefaultLangSys { + // Don't process the same language twice + continue + } + + lang, err := t.parseLangSys(b, record) + if err != nil { + return nil, err + } + + langs = append(langs, lang) + } + + return &Script{ + Tag: record.Tag, + DefaultLanguage: defaultLang, + Languages: langs, + }, nil +} + +// parseScriptList parses the ScriptList. +// See https://www.microsoft.com/typography/otspec/chapter2.htm#slTbl_sRec +func (t *TableLayout) parseScriptList() error { + offset := int(t.header.ScriptListOffset) + if offset >= len(t.bytes) { + return io.ErrUnexpectedEOF + } + + b := t.bytes[offset:] + r := bytes.NewReader(b) + + var count uint16 + if err := binary.Read(r, binary.BigEndian, &count); err != nil { + return fmt.Errorf("reading scriptCount: %s", err) + } + + t.Scripts = nil + for i := 0; i < int(count); i++ { + var record scriptRecord + if err := binary.Read(r, binary.BigEndian, &record); err != nil { + return fmt.Errorf("reading scriptRecord[%d]: %s", i, err) + } + + script, err := t.parseScript(b, record) + if err != nil { + return err + } + + t.Scripts = append(t.Scripts, script) + } + + return nil +} + +// parseFeature parses a single Feature table. b expected to be the beginning of FeatureList. +// See https://www.microsoft.com/typography/otspec/chapter2.htm#featTbl +func (t *TableLayout) parseFeature(b []byte, record featureRecord) (*Feature, error) { + if int(record.Offset) >= len(b) { + return nil, io.ErrUnexpectedEOF + } + + r := bytes.NewReader(b[record.Offset:]) + + var feature featureTable + if err := binary.Read(r, binary.BigEndian, &feature); err != nil { + return nil, fmt.Errorf("reading featureTable: %s", err) + } + + // TODO Read feature.FeatureParams and feature.LookupIndexCount + + return &Feature{ + Tag: record.Tag, + }, nil +} + +// parseFeatureList parses the FeatureList. +// See https://www.microsoft.com/typography/otspec/chapter2.htm#flTbl +func (t *TableLayout) parseFeatureList() error { + offset := int(t.header.FeatureListOffset) + if offset >= len(t.bytes) { + return io.ErrUnexpectedEOF + } + + b := t.bytes[offset:] + r := bytes.NewReader(b) + + var count uint16 + if err := binary.Read(r, binary.BigEndian, &count); err != nil { + return fmt.Errorf("reading featureCount: %s", err) + } + + t.Features = nil + for i := 0; i < int(count); i++ { + var record featureRecord + if err := binary.Read(r, binary.BigEndian, &record); err != nil { + return fmt.Errorf("reading featureRecord[%d]: %s", i, err) + } + + feature, err := t.parseFeature(b, record) + if err != nil { + return err + } + + t.Features = append(t.Features, feature) + } + + return nil +} + +// parseLookup parses a single Lookup table. b expected to be the beginning of LookupList. +// See https://www.microsoft.com/typography/otspec/chapter2.htm#featTbl +func (t *TableLayout) parseLookup(b []byte, record lookupRecord) (*Lookup, error) { + if int(record.Offset) >= len(b) { + return nil, io.ErrUnexpectedEOF + } + + r := bytes.NewReader(b[record.Offset:]) + + var lookup lookupTable + if err := binary.Read(r, binary.BigEndian, &lookup); err != nil { + return nil, fmt.Errorf("reading lookupTable: %s", err) + } + + // TODO Read lookup.Subtable + // TODO Read lookup.MarkFilteringSet + + return &Lookup{ + Type: lookup.Type, + Flag: lookup.Flag, // TODO Parse the type Enum + }, nil +} + +// parseLookupList parses the LookupList. +// See https://www.microsoft.com/typography/otspec/chapter2.htm#lulTbl +func (t *TableLayout) parseLookupList() error { + offset := int(t.header.LookupListOffset) + if offset >= len(t.bytes) { + return io.ErrUnexpectedEOF + } + + b := t.bytes[offset:] + r := bytes.NewReader(b) + + var count uint16 + if err := binary.Read(r, binary.BigEndian, &count); err != nil { + return fmt.Errorf("reading lookupCount: %s", err) + } + + t.Lookups = nil + for i := 0; i < int(count); i++ { + var record lookupRecord + if err := binary.Read(r, binary.BigEndian, &record); err != nil { + return fmt.Errorf("reading lookupRecord[%d]: %s", i, err) + } + + lookup, err := t.parseLookup(b, record) + if err != nil { + return err + } + + t.Lookups = append(t.Lookups, lookup) + } + + return nil +} + +// parseTableLayout parses a common Layout Table used by GPOS and GSUB. +func parseTableLayout(buffer io.Reader) (*TableLayout, error) { + b, err := ioutil.ReadAll(buffer) + if err != nil { + return nil, err + } + t := &TableLayout{ + bytes: b, + } + + r := bytes.NewReader(b) + if err := binary.Read(r, binary.BigEndian, &t.version); err != nil { + return nil, fmt.Errorf("reading layout version header: %s", err) + } + + if t.version.Major != 1 || (t.version.Minor != 0 && t.version.Minor != 1) { + return nil, fmt.Errorf("unsupported layout version (major: %d, minor: %d)", t.version.Major, t.version.Minor) + } + + switch t.version.Minor { + case 0: + if err := binary.Read(r, binary.BigEndian, &t.header.layoutHeader10); err != nil { + return nil, fmt.Errorf("reading layout header: %s", err) + } + case 1: + if err := binary.Read(r, binary.BigEndian, &t.header); err != nil { + return nil, fmt.Errorf("reading layout header: %s", err) + } + default: + // Should never get here, because we are gated by a earlier check. + panic("unsupported minor version") + } + + if err := t.parseLookupList(); err != nil { + return nil, err + } + + if err := t.parseFeatureList(); err != nil { + return nil, err + } + + if err := t.parseScriptList(); err != nil { + return nil, err + } + + return t, nil +} diff --git a/sfnt/tag.go b/sfnt/tag.go index 6f1919d..cb0b74c 100644 --- a/sfnt/tag.go +++ b/sfnt/tag.go @@ -15,6 +15,10 @@ var ( TagOS2 = NamedTag("OS/2") // TagName represents the 'name' table, which contains font name information TagName = NamedTag("name") + // TagGpos represents the 'GPOS' table, which contains Glyph Positioning features + TagGpos = NamedTag("GPOS") + // TagGsub represents the 'GSUB' table, which contains Glyph Substitution features + TagGsub = NamedTag("GSUB") // TypeTrueType is the first four bytes of an OpenType file containing a TrueType font TypeTrueType = Tag{0x00010000} From b6436c3041903f8ad0c9cc266043010d2097e51e Mon Sep 17 00:00:00 2001 From: Andrew Brampton Date: Sun, 21 Jan 2018 08:58:12 -0800 Subject: [PATCH 3/7] Added a new "features" command for displaying the font features. --- commands/features.go | 54 ++++++++++++++++++++++++++++++++++++++++++++ commands/info.go | 1 - main.go | 9 +++++--- 3 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 commands/features.go diff --git a/commands/features.go b/commands/features.go new file mode 100644 index 0000000..783d9b5 --- /dev/null +++ b/commands/features.go @@ -0,0 +1,54 @@ +package commands + +import ( + "fmt" + "os" + + "github.com/ConradIrwin/font/sfnt" +) + +func layoutTable(font *sfnt.Font, tag sfnt.Tag, name string) { + if font.HasTable(tag) { + fmt.Printf("%s:\n", name) + + t := font.Table(tag).(*sfnt.TableLayout) + for _, script := range t.Scripts { + fmt.Printf("\tScript %q (%s):\n", script.Tag, script.String()) + + fmt.Printf("\t\tDefault Language:\n") + for _, feature := range script.DefaultLanguage.Features { + fmt.Printf("\t\t\tFeature %q (%s)\n", feature.Tag, feature.String()) + } + + for _, lang := range script.Languages { + fmt.Printf("\t\tLanguage %q (%s):\n", lang.Tag, lang.String()) + for _, feature := range lang.Features { + fmt.Printf("\t\t\tFeature %q (%s)\n", feature.Tag, feature.String()) + } + } + } + } else { + fmt.Printf("No %s\n", name) + } +} + +// Features prints the gpos/gsub tables (contins font features). +func Features() { + if len(os.Args) < 2 { + panic(fmt.Errorf("Specify a font file")) + } + + file, err := os.Open(os.Args[1]) + if err != nil { + panic(fmt.Errorf("Failed to open font: %s", err)) + } + defer file.Close() + + font, err := sfnt.Parse(file) + if err != nil { + panic(fmt.Errorf("Failed to parse font: %s", err)) + } + + layoutTable(font, sfnt.TagGsub, "Glyph Substitution Table (GSUB)") + layoutTable(font, sfnt.TagGpos, "Glyph Positioning Table (GPOS)") +} diff --git a/commands/info.go b/commands/info.go index f30197f..7cc4fd2 100644 --- a/commands/info.go +++ b/commands/info.go @@ -11,7 +11,6 @@ import ( ) func Info() { - file, err := os.Open(os.Args[1]) if err != nil { panic(err) diff --git a/main.go b/main.go index c05a8e6..1714204 100644 --- a/main.go +++ b/main.go @@ -25,14 +25,17 @@ func main() { commands.Stats() case "metrics": commands.Metrics() + case "features": + commands.Features() default: fmt.Println(` -Usage: font [scrub|info|stats] font.[otf,ttf,woff] +Usage: font [features|info|metrics|scrub|stats] font.[otf,ttf,woff] +features: prints the gpos/gsub tables (contins font features) info: prints the name table (contains metadata) metrics: prints the hhea table (contains font metrics) -stats: prints each table and the amount of space used -scrub: remove the name table (saves significant space)`) +scrub: remove the name table (saves significant space) +stats: prints each table and the amount of space used`) } } From 980a891a7f0bb9381057a7f1b7b537a4f65f0b67 Mon Sep 17 00:00:00 2001 From: Andrew Brampton Date: Sun, 21 Jan 2018 12:51:19 -0800 Subject: [PATCH 4/7] Minor tweak due to code review feedback. --- commands/features.go | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/commands/features.go b/commands/features.go index 783d9b5..2e8a4b0 100644 --- a/commands/features.go +++ b/commands/features.go @@ -7,6 +7,27 @@ import ( "github.com/ConradIrwin/font/sfnt" ) +// Features prints the gpos/gsub tables (contins font features). +func Features() { + if len(os.Args) < 2 { + panic(fmt.Errorf("Specify a font file")) + } + + file, err := os.Open(os.Args[1]) + if err != nil { + panic(fmt.Errorf("Failed to open font: %s", err)) + } + defer file.Close() + + font, err := sfnt.Parse(file) + if err != nil { + panic(fmt.Errorf("Failed to parse font: %s", err)) + } + + layoutTable(font, sfnt.TagGsub, "Glyph Substitution Table (GSUB)") + layoutTable(font, sfnt.TagGpos, "Glyph Positioning Table (GPOS)") +} + func layoutTable(font *sfnt.Font, tag sfnt.Tag, name string) { if font.HasTable(tag) { fmt.Printf("%s:\n", name) @@ -31,24 +52,3 @@ func layoutTable(font *sfnt.Font, tag sfnt.Tag, name string) { fmt.Printf("No %s\n", name) } } - -// Features prints the gpos/gsub tables (contins font features). -func Features() { - if len(os.Args) < 2 { - panic(fmt.Errorf("Specify a font file")) - } - - file, err := os.Open(os.Args[1]) - if err != nil { - panic(fmt.Errorf("Failed to open font: %s", err)) - } - defer file.Close() - - font, err := sfnt.Parse(file) - if err != nil { - panic(fmt.Errorf("Failed to parse font: %s", err)) - } - - layoutTable(font, sfnt.TagGsub, "Glyph Substitution Table (GSUB)") - layoutTable(font, sfnt.TagGpos, "Glyph Positioning Table (GPOS)") -} From 681d41486672312eff38761e5efb584465506082 Mon Sep 17 00:00:00 2001 From: Andrew Brampton Date: Sun, 21 Jan 2018 13:24:00 -0800 Subject: [PATCH 5/7] Fixed a couple of typos. --- commands/features.go | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/features.go b/commands/features.go index 2e8a4b0..56cbf6f 100644 --- a/commands/features.go +++ b/commands/features.go @@ -7,7 +7,7 @@ import ( "github.com/ConradIrwin/font/sfnt" ) -// Features prints the gpos/gsub tables (contins font features). +// Features prints the gpos/gsub tables (contains font features). func Features() { if len(os.Args) < 2 { panic(fmt.Errorf("Specify a font file")) diff --git a/main.go b/main.go index 1714204..54cf82f 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ func main() { fmt.Println(` Usage: font [features|info|metrics|scrub|stats] font.[otf,ttf,woff] -features: prints the gpos/gsub tables (contins font features) +features: prints the gpos/gsub tables (contains font features) info: prints the name table (contains metadata) metrics: prints the hhea table (contains font metrics) scrub: remove the name table (saves significant space) From 368e469d6286f4fba222fb2fa466caf3e1a67d45 Mon Sep 17 00:00:00 2001 From: Andrew Brampton Date: Mon, 22 Jan 2018 17:02:31 -0800 Subject: [PATCH 6/7] Change feature to only print the description of the field if it is known. --- commands/features.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/commands/features.go b/commands/features.go index 56cbf6f..59b0bb1 100644 --- a/commands/features.go +++ b/commands/features.go @@ -34,17 +34,17 @@ func layoutTable(font *sfnt.Font, tag sfnt.Tag, name string) { t := font.Table(tag).(*sfnt.TableLayout) for _, script := range t.Scripts { - fmt.Printf("\tScript %q (%s):\n", script.Tag, script.String()) + fmt.Printf("\tScript %q%s:\n", script.Tag, bracketString(script)) fmt.Printf("\t\tDefault Language:\n") for _, feature := range script.DefaultLanguage.Features { - fmt.Printf("\t\t\tFeature %q (%s)\n", feature.Tag, feature.String()) + fmt.Printf("\t\t\tFeature %q%s\n", feature.Tag, bracketString(feature)) } for _, lang := range script.Languages { - fmt.Printf("\t\tLanguage %q (%s):\n", lang.Tag, lang.String()) + fmt.Printf("\t\tLanguage %q%s:\n", lang.Tag, bracketString(lang)) for _, feature := range lang.Features { - fmt.Printf("\t\t\tFeature %q (%s)\n", feature.Tag, feature.String()) + fmt.Printf("\t\t\tFeature %q%s\n", feature.Tag, bracketString(feature)) } } } @@ -52,3 +52,10 @@ func layoutTable(font *sfnt.Font, tag sfnt.Tag, name string) { fmt.Printf("No %s\n", name) } } + +func bracketString(o fmt.Stringer) string { + if s := o.String(); s != "" { + return fmt.Sprintf(" (%s)", s) + } + return "" +} From f0147c0186f14af7a4a33c805664225e06e2b95b Mon Sep 17 00:00:00 2001 From: Andrew Brampton Date: Mon, 22 Jan 2018 17:03:11 -0800 Subject: [PATCH 7/7] Added support to print out "Character Variants N", and "Stylistic Set N" feature names. --- sfnt/names.go | 23 ++--------------------- sfnt/table_feature.go | 11 +++++++++-- sfnt/table_feature_test.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 sfnt/table_feature_test.go diff --git a/sfnt/names.go b/sfnt/names.go index 772ebaf..dee7ab5 100644 --- a/sfnt/names.go +++ b/sfnt/names.go @@ -784,7 +784,7 @@ var ( "crcy": "Currency (Deprecated)", "cswh": "Contextual Swash", "curs": "Cursive Positioning", - // "cv01"..."cv99": "Character Variants", // Handled in Feature.String() + // "cv01"..."cv99": "Character Variants N", // Handled in Feature.String() "dist": "Distances", "dlig": "Discretionary Ligatures", "dnom": "Denominators", @@ -864,26 +864,7 @@ var ( "size": "Optical size", "smcp": "Small Capitals", "smpl": "Simplified Forms", - "ss01": "Stylistic Set 1", - "ss02": "Stylistic Set 2", - "ss03": "Stylistic Set 3", - "ss04": "Stylistic Set 4", - "ss05": "Stylistic Set 5", - "ss06": "Stylistic Set 6", - "ss07": "Stylistic Set 7", - "ss08": "Stylistic Set 8", - "ss09": "Stylistic Set 9", - "ss10": "Stylistic Set 10", - "ss11": "Stylistic Set 11", - "ss12": "Stylistic Set 12", - "ss13": "Stylistic Set 13", - "ss14": "Stylistic Set 14", - "ss15": "Stylistic Set 15", - "ss16": "Stylistic Set 16", - "ss17": "Stylistic Set 17", - "ss18": "Stylistic Set 18", - "ss19": "Stylistic Set 19", - "ss20": "Stylistic Set 20", + //"ss01"..."ss20": "Stylistic Set N", , // Handled in Feature.String() "ssty": "Math script style alternates", "stch": "Stretching Glyph Decomposition", "subs": "Subscript", diff --git a/sfnt/table_feature.go b/sfnt/table_feature.go index f512507..7c11b2f 100644 --- a/sfnt/table_feature.go +++ b/sfnt/table_feature.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/ioutil" + "strconv" ) // TableLayout represents the common layout table used by GPOS and GSUB. @@ -66,8 +67,14 @@ func (f *Feature) String() string { return name } - if len(tag) == 4 && tag[0] == 'c' && tag[1] == 'v' && tag[2] >= '0' && tag[2] <= '9' && tag[3] >= '0' && tag[3] <= '9' { - return "Character Variants" + if len(tag) == 4 && (tag[0:2] == "cv" || tag[0:2] == "ss") { + if i, err := strconv.Atoi(tag[2:4]); err == nil { + if tag[0:2] == "cv" && i >= 1 && i <= 99 { + return fmt.Sprintf("Character Variant %d", i) + } else if tag[0:2] == "ss" && i >= 1 && i <= 20 { + return fmt.Sprintf("Stylistic Set %d", i) + } + } } return "" diff --git a/sfnt/table_feature_test.go b/sfnt/table_feature_test.go new file mode 100644 index 0000000..ddc2c33 --- /dev/null +++ b/sfnt/table_feature_test.go @@ -0,0 +1,30 @@ +package sfnt + +import ( + "testing" +) + +func TestFeatureString(t *testing.T) { + tests := []struct { + tag string + want string + }{ + {"aalt", "Access All Alternates"}, + {"zhtw", "Traditional Chinese Forms (Deprecated)"}, + {"ss01", "Stylistic Set 1"}, + {"ss20", "Stylistic Set 20"}, + {"cv01", "Character Variant 1"}, + {"cv99", "Character Variant 99"}, + {"BLAH", ""}, + {"cv00", ""}, + {"ss00", ""}, + {"ss21", ""}, + } + + for _, test := range tests { + f := Feature{Tag: NamedTag(test.tag)} + if got := f.String(); got != test.want { + t.Errorf("Feature{%q}.String() = %q want %q", test.tag, got, test.want) + } + } +}