LCOV - code coverage report
Current view: directory - gfx/harfbuzz/src - hb-ot-tag.cc (source / functions) Found Hit Coverage
Test: app.info Lines: 118 0 0.0 %
Date: 2012-06-02 Functions: 10 0 0.0 %

       1                 : /*
       2                 :  * Copyright © 2009  Red Hat, Inc.
       3                 :  * Copyright © 2011  Google, Inc.
       4                 :  *
       5                 :  *  This is part of HarfBuzz, a text shaping library.
       6                 :  *
       7                 :  * Permission is hereby granted, without written agreement and without
       8                 :  * license or royalty fees, to use, copy, modify, and distribute this
       9                 :  * software and its documentation for any purpose, provided that the
      10                 :  * above copyright notice and the following two paragraphs appear in
      11                 :  * all copies of this software.
      12                 :  *
      13                 :  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
      14                 :  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
      15                 :  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
      16                 :  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
      17                 :  * DAMAGE.
      18                 :  *
      19                 :  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
      20                 :  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
      21                 :  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
      22                 :  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
      23                 :  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
      24                 :  *
      25                 :  * Red Hat Author(s): Behdad Esfahbod
      26                 :  * Google Author(s): Behdad Esfahbod
      27                 :  */
      28                 : 
      29                 : #include "hb-private.hh"
      30                 : #include "hb-ot.h"
      31                 : 
      32                 : #include <string.h>
      33                 : 
      34                 : 
      35                 : 
      36                 : /* hb_script_t */
      37                 : 
      38                 : static hb_tag_t
      39               0 : hb_ot_old_tag_from_script (hb_script_t script)
      40                 : {
      41               0 :   switch ((hb_tag_t) script) {
      42               0 :     case HB_SCRIPT_INVALID:             return HB_OT_TAG_DEFAULT_SCRIPT;
      43                 : 
      44                 :     /* KATAKANA and HIRAGANA both map to 'kana' */
      45               0 :     case HB_SCRIPT_HIRAGANA:            return HB_TAG('k','a','n','a');
      46                 : 
      47                 :     /* Spaces at the end are preserved, unlike ISO 15924 */
      48               0 :     case HB_SCRIPT_LAO:                 return HB_TAG('l','a','o',' ');
      49               0 :     case HB_SCRIPT_YI:                  return HB_TAG('y','i',' ',' ');
      50                 :     /* Unicode-5.0 additions */
      51               0 :     case HB_SCRIPT_NKO:                 return HB_TAG('n','k','o',' ');
      52                 :     /* Unicode-5.1 additions */
      53               0 :     case HB_SCRIPT_VAI:                 return HB_TAG('v','a','i',' ');
      54                 :     /* Unicode-5.2 additions */
      55                 :     /* Unicode-6.0 additions */
      56                 :   }
      57                 : 
      58                 :   /* Else, just change first char to lowercase and return */
      59               0 :   return ((hb_tag_t) script) | 0x20000000;
      60                 : }
      61                 : 
      62                 : static hb_script_t
      63               0 : hb_ot_old_tag_to_script (hb_tag_t tag)
      64                 : {
      65               0 :   if (unlikely (tag == HB_OT_TAG_DEFAULT_SCRIPT))
      66               0 :     return HB_SCRIPT_INVALID;
      67                 : 
      68                 :   /* This side of the conversion is fully algorithmic. */
      69                 : 
      70                 :   /* Any spaces at the end of the tag are replaced by repeating the last
      71                 :    * letter.  Eg 'nko ' -> 'Nkoo' */
      72               0 :   if (unlikely ((tag & 0x0000FF00) == 0x00002000))
      73               0 :     tag |= (tag >> 8) & 0x0000FF00; /* Copy second letter to third */
      74               0 :   if (unlikely ((tag & 0x000000FF) == 0x00000020))
      75               0 :     tag |= (tag >> 8) & 0x000000FF; /* Copy third letter to fourth */
      76                 : 
      77                 :   /* Change first char to uppercase and return */
      78               0 :   return (hb_script_t) (tag & ~0x20000000);
      79                 : }
      80                 : 
      81                 : static hb_tag_t
      82               0 : hb_ot_new_tag_from_script (hb_script_t script)
      83                 : {
      84               0 :   switch ((hb_tag_t) script) {
      85               0 :     case HB_SCRIPT_BENGALI:             return HB_TAG('b','n','g','2');
      86               0 :     case HB_SCRIPT_DEVANAGARI:          return HB_TAG('d','e','v','2');
      87               0 :     case HB_SCRIPT_GUJARATI:            return HB_TAG('g','j','r','2');
      88               0 :     case HB_SCRIPT_GURMUKHI:            return HB_TAG('g','u','r','2');
      89               0 :     case HB_SCRIPT_KANNADA:             return HB_TAG('k','n','d','2');
      90               0 :     case HB_SCRIPT_MALAYALAM:           return HB_TAG('m','l','m','2');
      91               0 :     case HB_SCRIPT_ORIYA:               return HB_TAG('o','r','y','2');
      92               0 :     case HB_SCRIPT_TAMIL:               return HB_TAG('t','m','l','2');
      93               0 :     case HB_SCRIPT_TELUGU:              return HB_TAG('t','e','l','2');
      94                 :   }
      95                 : 
      96               0 :   return HB_OT_TAG_DEFAULT_SCRIPT;
      97                 : }
      98                 : 
      99                 : static hb_script_t
     100               0 : hb_ot_new_tag_to_script (hb_tag_t tag)
     101                 : {
     102               0 :   switch (tag) {
     103               0 :     case HB_TAG('b','n','g','2'):       return HB_SCRIPT_BENGALI;
     104               0 :     case HB_TAG('d','e','v','2'):       return HB_SCRIPT_DEVANAGARI;
     105               0 :     case HB_TAG('g','j','r','2'):       return HB_SCRIPT_GUJARATI;
     106               0 :     case HB_TAG('g','u','r','2'):       return HB_SCRIPT_GURMUKHI;
     107               0 :     case HB_TAG('k','n','d','2'):       return HB_SCRIPT_KANNADA;
     108               0 :     case HB_TAG('m','l','m','2'):       return HB_SCRIPT_MALAYALAM;
     109               0 :     case HB_TAG('o','r','y','2'):       return HB_SCRIPT_ORIYA;
     110               0 :     case HB_TAG('t','m','l','2'):       return HB_SCRIPT_TAMIL;
     111               0 :     case HB_TAG('t','e','l','2'):       return HB_SCRIPT_TELUGU;
     112                 :   }
     113                 : 
     114               0 :   return HB_SCRIPT_UNKNOWN;
     115                 : }
     116                 : 
     117                 : /*
     118                 :  * Complete list at:
     119                 :  * https://www.microsoft.com/typography/otspec/scripttags.htm
     120                 :  * https://www.microsoft.com/typography/otspec160/scripttagsProposed.htm
     121                 :  *
     122                 :  * Most of the script tags are the same as the ISO 15924 tag but lowercased.
     123                 :  * So we just do that, and handle the exceptional cases in a switch.
     124                 :  */
     125                 : 
     126                 : void
     127               0 : hb_ot_tags_from_script (hb_script_t  script,
     128                 :                         hb_tag_t    *script_tag_1,
     129                 :                         hb_tag_t    *script_tag_2)
     130                 : {
     131                 :   hb_tag_t new_tag;
     132                 : 
     133               0 :   *script_tag_2 = HB_OT_TAG_DEFAULT_SCRIPT;
     134               0 :   *script_tag_1 = hb_ot_old_tag_from_script (script);
     135                 : 
     136               0 :   new_tag = hb_ot_new_tag_from_script (script);
     137               0 :   if (unlikely (new_tag != HB_OT_TAG_DEFAULT_SCRIPT)) {
     138               0 :     *script_tag_2 = *script_tag_1;
     139               0 :     *script_tag_1 = new_tag;
     140                 :   }
     141               0 : }
     142                 : 
     143                 : hb_script_t
     144               0 : hb_ot_tag_to_script (hb_tag_t tag)
     145                 : {
     146               0 :   if (unlikely ((tag & 0x000000FF) == '2'))
     147               0 :     return hb_ot_new_tag_to_script (tag);
     148                 : 
     149               0 :   return hb_ot_old_tag_to_script (tag);
     150                 : }
     151                 : 
     152                 : 
     153                 : /* hb_language_t */
     154                 : 
     155                 : typedef struct {
     156                 :   char language[6];
     157                 :   hb_tag_t tag;
     158                 : } LangTag;
     159                 : 
     160                 : /*
     161                 :  * Complete list at:
     162                 :  * http://www.microsoft.com/typography/otspec/languagetags.htm
     163                 :  *
     164                 :  * Generated by intersecting the OpenType language tag list from
     165                 :  * Draft OpenType 1.5 spec, with with the ISO 639-3 codes from
     166                 :  * 2008/08/04, matching on name, and finally adjusted manually.
     167                 :  *
     168                 :  * Many items still missing.  Those are commented out at the end.
     169                 :  * Keep sorted for bsearch.
     170                 :  */
     171                 : 
     172                 : static const LangTag ot_languages[] = {
     173                 :   {"aa",      HB_TAG('A','F','R',' ')},       /* Afar */
     174                 :   {"ab",      HB_TAG('A','B','K',' ')},       /* Abkhazian */
     175                 :   {"abq",     HB_TAG('A','B','A',' ')},       /* Abaza */
     176                 :   {"ady",     HB_TAG('A','D','Y',' ')},       /* Adyghe */
     177                 :   {"af",      HB_TAG('A','F','K',' ')},       /* Afrikaans */
     178                 :   {"aiw",     HB_TAG('A','R','I',' ')},       /* Aari */
     179                 :   {"am",      HB_TAG('A','M','H',' ')},       /* Amharic */
     180                 :   {"ar",      HB_TAG('A','R','A',' ')},       /* Arabic */
     181                 :   {"arn",     HB_TAG('M','A','P',' ')},       /* Mapudungun */
     182                 :   {"as",      HB_TAG('A','S','M',' ')},       /* Assamese */
     183                 :   {"av",      HB_TAG('A','V','R',' ')},       /* Avaric */
     184                 :   {"awa",     HB_TAG('A','W','A',' ')},       /* Awadhi */
     185                 :   {"ay",      HB_TAG('A','Y','M',' ')},       /* Aymara */
     186                 :   {"az",      HB_TAG('A','Z','E',' ')},       /* Azerbaijani */
     187                 :   {"ba",      HB_TAG('B','S','H',' ')},       /* Bashkir */
     188                 :   {"bal",     HB_TAG('B','L','I',' ')},       /* Baluchi */
     189                 :   {"bcq",     HB_TAG('B','C','H',' ')},       /* Bench */
     190                 :   {"bem",     HB_TAG('B','E','M',' ')},       /* Bemba (Zambia) */
     191                 :   {"bfq",     HB_TAG('B','A','D',' ')},       /* Badaga */
     192                 :   {"bft",     HB_TAG('B','L','T',' ')},       /* Balti */
     193                 :   {"bg",      HB_TAG('B','G','R',' ')},       /* Bulgarian */
     194                 :   {"bhb",     HB_TAG('B','H','I',' ')},       /* Bhili */
     195                 :   {"bho",     HB_TAG('B','H','O',' ')},       /* Bhojpuri */
     196                 :   {"bik",     HB_TAG('B','I','K',' ')},       /* Bikol */
     197                 :   {"bin",     HB_TAG('E','D','O',' ')},       /* Bini */
     198                 :   {"bm",      HB_TAG('B','M','B',' ')},       /* Bambara */
     199                 :   {"bn",      HB_TAG('B','E','N',' ')},       /* Bengali */
     200                 :   {"bo",      HB_TAG('T','I','B',' ')},       /* Tibetan */
     201                 :   {"br",      HB_TAG('B','R','E',' ')},       /* Breton */
     202                 :   {"brh",     HB_TAG('B','R','H',' ')},       /* Brahui */
     203                 :   {"bs",      HB_TAG('B','O','S',' ')},       /* Bosnian */
     204                 :   {"btb",     HB_TAG('B','T','I',' ')},       /* Beti (Cameroon) */
     205                 :   {"ca",      HB_TAG('C','A','T',' ')},       /* Catalan */
     206                 :   {"ce",      HB_TAG('C','H','E',' ')},       /* Chechen */
     207                 :   {"ceb",     HB_TAG('C','E','B',' ')},       /* Cebuano */
     208                 :   {"chp",     HB_TAG('C','H','P',' ')},       /* Chipewyan */
     209                 :   {"chr",     HB_TAG('C','H','R',' ')},       /* Cherokee */
     210                 :   {"cop",     HB_TAG('C','O','P',' ')},       /* Coptic */
     211                 :   {"cr",      HB_TAG('C','R','E',' ')},       /* Cree */
     212                 :   {"crh",     HB_TAG('C','R','T',' ')},       /* Crimean Tatar */
     213                 :   {"crm",     HB_TAG('M','C','R',' ')},       /* Moose Cree */
     214                 :   {"crx",     HB_TAG('C','R','R',' ')},       /* Carrier */
     215                 :   {"cs",      HB_TAG('C','S','Y',' ')},       /* Czech */
     216                 :   {"cu",      HB_TAG('C','S','L',' ')},       /* Church Slavic */
     217                 :   {"cv",      HB_TAG('C','H','U',' ')},       /* Chuvash */
     218                 :   {"cwd",     HB_TAG('D','C','R',' ')},       /* Woods Cree */
     219                 :   {"cy",      HB_TAG('W','E','L',' ')},       /* Welsh */
     220                 :   {"da",      HB_TAG('D','A','N',' ')},       /* Danish */
     221                 :   {"dap",     HB_TAG('N','I','S',' ')},       /* Nisi (India) */
     222                 :   {"dar",     HB_TAG('D','A','R',' ')},       /* Dargwa */
     223                 :   {"de",      HB_TAG('D','E','U',' ')},       /* German */
     224                 :   {"din",     HB_TAG('D','N','K',' ')},       /* Dinka */
     225                 :   {"dng",     HB_TAG('D','U','N',' ')},       /* Dungan */
     226                 :   {"doi",     HB_TAG('D','G','R',' ')},       /* Dogri */
     227                 :   {"dsb",     HB_TAG('L','S','B',' ')},       /* Lower Sorbian */
     228                 :   {"dv",      HB_TAG('D','I','V',' ')},       /* Dhivehi */
     229                 :   {"dz",      HB_TAG('D','Z','N',' ')},       /* Dzongkha */
     230                 :   {"ee",      HB_TAG('E','W','E',' ')},       /* Ewe */
     231                 :   {"efi",     HB_TAG('E','F','I',' ')},       /* Efik */
     232                 :   {"el",      HB_TAG('E','L','L',' ')},       /* Modern Greek (1453-) */
     233                 :   {"en",      HB_TAG('E','N','G',' ')},       /* English */
     234                 :   {"eo",      HB_TAG('N','T','O',' ')},       /* Esperanto */
     235                 :   {"eot",     HB_TAG('B','T','I',' ')},       /* Beti (Côte d'Ivoire) */
     236                 :   {"es",      HB_TAG('E','S','P',' ')},       /* Spanish */
     237                 :   {"et",      HB_TAG('E','T','I',' ')},       /* Estonian */
     238                 :   {"eu",      HB_TAG('E','U','Q',' ')},       /* Basque */
     239                 :   {"eve",     HB_TAG('E','V','N',' ')},       /* Even */
     240                 :   {"evn",     HB_TAG('E','V','K',' ')},       /* Evenki */
     241                 :   {"fa",      HB_TAG('F','A','R',' ')},       /* Persian */
     242                 :   {"ff",      HB_TAG('F','U','L',' ')},       /* Fulah */
     243                 :   {"fi",      HB_TAG('F','I','N',' ')},       /* Finnish */
     244                 :   {"fil",     HB_TAG('P','I','L',' ')},       /* Filipino */
     245                 :   {"fj",      HB_TAG('F','J','I',' ')},       /* Fijian */
     246                 :   {"fo",      HB_TAG('F','O','S',' ')},       /* Faroese */
     247                 :   {"fon",     HB_TAG('F','O','N',' ')},       /* Fon */
     248                 :   {"fr",      HB_TAG('F','R','A',' ')},       /* French */
     249                 :   {"fur",     HB_TAG('F','R','L',' ')},       /* Friulian */
     250                 :   {"fy",      HB_TAG('F','R','I',' ')},       /* Western Frisian */
     251                 :   {"ga",      HB_TAG('I','R','I',' ')},       /* Irish */
     252                 :   {"gaa",     HB_TAG('G','A','D',' ')},       /* Ga */
     253                 :   {"gag",     HB_TAG('G','A','G',' ')},       /* Gagauz */
     254                 :   {"gbm",     HB_TAG('G','A','W',' ')},       /* Garhwali */
     255                 :   {"gd",      HB_TAG('G','A','E',' ')},       /* Scottish Gaelic */
     256                 :   {"gl",      HB_TAG('G','A','L',' ')},       /* Galician */
     257                 :   {"gld",     HB_TAG('N','A','N',' ')},       /* Nanai */
     258                 :   {"gn",      HB_TAG('G','U','A',' ')},       /* Guarani */
     259                 :   {"gon",     HB_TAG('G','O','N',' ')},       /* Gondi */
     260                 :   {"grt",     HB_TAG('G','R','O',' ')},       /* Garo */
     261                 :   {"gu",      HB_TAG('G','U','J',' ')},       /* Gujarati */
     262                 :   {"guk",     HB_TAG('G','M','Z',' ')},       /* Gumuz */
     263                 :   {"gv",      HB_TAG('M','N','X',' ')},       /* Manx Gaelic */
     264                 :   {"ha",      HB_TAG('H','A','U',' ')},       /* Hausa */
     265                 :   {"har",     HB_TAG('H','R','I',' ')},       /* Harari */
     266                 :   {"he",      HB_TAG('I','W','R',' ')},       /* Hebrew */
     267                 :   {"hi",      HB_TAG('H','I','N',' ')},       /* Hindi */
     268                 :   {"hil",     HB_TAG('H','I','L',' ')},       /* Hiligaynon */
     269                 :   {"hoc",     HB_TAG('H','O',' ',' ')},       /* Ho */
     270                 :   {"hr",      HB_TAG('H','R','V',' ')},       /* Croatian */
     271                 :   {"hsb",     HB_TAG('U','S','B',' ')},       /* Upper Sorbian */
     272                 :   {"ht",      HB_TAG('H','A','I',' ')},       /* Haitian */
     273                 :   {"hu",      HB_TAG('H','U','N',' ')},       /* Hungarian */
     274                 :   {"hy",      HB_TAG('H','Y','E',' ')},       /* Armenian */
     275                 :   {"id",      HB_TAG('I','N','D',' ')},       /* Indonesian */
     276                 :   {"ig",      HB_TAG('I','B','O',' ')},       /* Igbo */
     277                 :   {"igb",     HB_TAG('E','B','I',' ')},       /* Ebira */
     278                 :   {"inh",     HB_TAG('I','N','G',' ')},       /* Ingush */
     279                 :   {"is",      HB_TAG('I','S','L',' ')},       /* Icelandic */
     280                 :   {"it",      HB_TAG('I','T','A',' ')},       /* Italian */
     281                 :   {"iu",      HB_TAG('I','N','U',' ')},       /* Inuktitut */
     282                 :   {"ja",      HB_TAG('J','A','N',' ')},       /* Japanese */
     283                 :   {"jv",      HB_TAG('J','A','V',' ')},       /* Javanese */
     284                 :   {"ka",      HB_TAG('K','A','T',' ')},       /* Georgian */
     285                 :   {"kam",     HB_TAG('K','M','B',' ')},       /* Kamba (Kenya) */
     286                 :   {"kbd",     HB_TAG('K','A','B',' ')},       /* Kabardian */
     287                 :   {"kdr",     HB_TAG('K','R','M',' ')},       /* Karaim */
     288                 :   {"kdt",     HB_TAG('K','U','Y',' ')},       /* Kuy */
     289                 :   {"kfr",     HB_TAG('K','A','C',' ')},       /* Kachchi */
     290                 :   {"kfy",     HB_TAG('K','M','N',' ')},       /* Kumaoni */
     291                 :   {"kha",     HB_TAG('K','S','I',' ')},       /* Khasi */
     292                 :   {"khw",     HB_TAG('K','H','W',' ')},       /* Khowar */
     293                 :   {"ki",      HB_TAG('K','I','K',' ')},       /* Kikuyu */
     294                 :   {"kk",      HB_TAG('K','A','Z',' ')},       /* Kazakh */
     295                 :   {"kl",      HB_TAG('G','R','N',' ')},       /* Kalaallisut */
     296                 :   {"kln",     HB_TAG('K','A','L',' ')},       /* Kalenjin */
     297                 :   {"km",      HB_TAG('K','H','M',' ')},       /* Central Khmer */
     298                 :   {"kmw",     HB_TAG('K','M','O',' ')},       /* Komo (Democratic Republic of Congo) */
     299                 :   {"kn",      HB_TAG('K','A','N',' ')},       /* Kannada */
     300                 :   {"ko",      HB_TAG('K','O','R',' ')},       /* Korean */
     301                 :   {"koi",     HB_TAG('K','O','P',' ')},       /* Komi-Permyak */
     302                 :   {"kok",     HB_TAG('K','O','K',' ')},       /* Konkani */
     303                 :   {"kpe",     HB_TAG('K','P','L',' ')},       /* Kpelle */
     304                 :   {"kpv",     HB_TAG('K','O','Z',' ')},       /* Komi-Zyrian */
     305                 :   {"kpy",     HB_TAG('K','Y','K',' ')},       /* Koryak */
     306                 :   {"kqy",     HB_TAG('K','R','T',' ')},       /* Koorete */
     307                 :   {"kr",      HB_TAG('K','N','R',' ')},       /* Kanuri */
     308                 :   {"kri",     HB_TAG('K','R','I',' ')},       /* Krio */
     309                 :   {"krl",     HB_TAG('K','R','L',' ')},       /* Karelian */
     310                 :   {"kru",     HB_TAG('K','U','U',' ')},       /* Kurukh */
     311                 :   {"ks",      HB_TAG('K','S','H',' ')},       /* Kashmiri */
     312                 :   {"ku",      HB_TAG('K','U','R',' ')},       /* Kurdish */
     313                 :   {"kum",     HB_TAG('K','U','M',' ')},       /* Kumyk */
     314                 :   {"kvd",     HB_TAG('K','U','I',' ')},       /* Kui (Indonesia) */
     315                 :   {"kxu",     HB_TAG('K','U','I',' ')},       /* Kui (India) */
     316                 :   {"ky",      HB_TAG('K','I','R',' ')},       /* Kirghiz */
     317                 :   {"la",      HB_TAG('L','A','T',' ')},       /* Latin */
     318                 :   {"lad",     HB_TAG('J','U','D',' ')},       /* Ladino */
     319                 :   {"lb",      HB_TAG('L','T','Z',' ')},       /* Luxembourgish */
     320                 :   {"lbe",     HB_TAG('L','A','K',' ')},       /* Lak */
     321                 :   {"lbj",     HB_TAG('L','D','K',' ')},       /* Ladakhi */
     322                 :   {"lif",     HB_TAG('L','M','B',' ')},       /* Limbu */
     323                 :   {"lld",     HB_TAG('L','A','D',' ')},       /* Ladin */
     324                 :   {"ln",      HB_TAG('L','I','N',' ')},       /* Lingala */
     325                 :   {"lo",      HB_TAG('L','A','O',' ')},       /* Lao */
     326                 :   {"lt",      HB_TAG('L','T','H',' ')},       /* Lithuanian */
     327                 :   {"luo",     HB_TAG('L','U','O',' ')},       /* Luo (Kenya and Tanzania) */
     328                 :   {"luw",     HB_TAG('L','U','O',' ')},       /* Luo (Cameroon) */
     329                 :   {"lv",      HB_TAG('L','V','I',' ')},       /* Latvian */
     330                 :   {"lzz",     HB_TAG('L','A','Z',' ')},       /* Laz */
     331                 :   {"mai",     HB_TAG('M','T','H',' ')},       /* Maithili */
     332                 :   {"mdc",     HB_TAG('M','L','E',' ')},       /* Male (Papua New Guinea) */
     333                 :   {"mdf",     HB_TAG('M','O','K',' ')},       /* Moksha */
     334                 :   {"mdy",     HB_TAG('M','L','E',' ')},       /* Male (Ethiopia) */
     335                 :   {"men",     HB_TAG('M','D','E',' ')},       /* Mende (Sierra Leone) */
     336                 :   {"mg",      HB_TAG('M','L','G',' ')},       /* Malagasy */
     337                 :   {"mi",      HB_TAG('M','R','I',' ')},       /* Maori */
     338                 :   {"mk",      HB_TAG('M','K','D',' ')},       /* Macedonian */
     339                 :   {"ml",      HB_TAG('M','L','R',' ')},       /* Malayalam */
     340                 :   {"mn",      HB_TAG('M','N','G',' ')},       /* Mongolian */
     341                 :   {"mnc",     HB_TAG('M','C','H',' ')},       /* Manchu */
     342                 :   {"mni",     HB_TAG('M','N','I',' ')},       /* Manipuri */
     343                 :   {"mnk",     HB_TAG('M','N','D',' ')},       /* Mandinka */
     344                 :   {"mns",     HB_TAG('M','A','N',' ')},       /* Mansi */
     345                 :   {"mnw",     HB_TAG('M','O','N',' ')},       /* Mon */
     346                 :   {"mo",      HB_TAG('M','O','L',' ')},       /* Moldavian */
     347                 :   {"moh",     HB_TAG('M','O','H',' ')},       /* Mohawk */
     348                 :   {"mpe",     HB_TAG('M','A','J',' ')},       /* Majang */
     349                 :   {"mr",      HB_TAG('M','A','R',' ')},       /* Marathi */
     350                 :   {"ms",      HB_TAG('M','L','Y',' ')},       /* Malay */
     351                 :   {"mt",      HB_TAG('M','T','S',' ')},       /* Maltese */
     352                 :   {"mwr",     HB_TAG('M','A','W',' ')},       /* Marwari */
     353                 :   {"my",      HB_TAG('B','R','M',' ')},       /* Burmese */
     354                 :   {"mym",     HB_TAG('M','E','N',' ')},       /* Me'en */
     355                 :   {"myv",     HB_TAG('E','R','Z',' ')},       /* Erzya */
     356                 :   {"nb",      HB_TAG('N','O','R',' ')},       /* Norwegian Bokmål */
     357                 :   {"nco",     HB_TAG('S','I','B',' ')},       /* Sibe */
     358                 :   {"ne",      HB_TAG('N','E','P',' ')},       /* Nepali */
     359                 :   {"new",     HB_TAG('N','E','W',' ')},       /* Newari */
     360                 :   {"ng",      HB_TAG('N','D','G',' ')},       /* Ndonga */
     361                 :   {"ngl",     HB_TAG('L','M','W',' ')},       /* Lomwe */
     362                 :   {"niu",     HB_TAG('N','I','U',' ')},       /* Niuean */
     363                 :   {"niv",     HB_TAG('G','I','L',' ')},       /* Gilyak */
     364                 :   {"nl",      HB_TAG('N','L','D',' ')},       /* Dutch */
     365                 :   {"nn",      HB_TAG('N','Y','N',' ')},       /* Norwegian Nynorsk */
     366                 :   {"no",      HB_TAG('N','O','R',' ')},       /* Norwegian (deprecated) */
     367                 :   {"nog",     HB_TAG('N','O','G',' ')},       /* Nogai */
     368                 :   {"nqo",     HB_TAG('N','K','O',' ')},       /* N'Ko */
     369                 :   {"nsk",     HB_TAG('N','A','S',' ')},       /* Naskapi */
     370                 :   {"ny",      HB_TAG('C','H','I',' ')},       /* Nyanja */
     371                 :   {"oc",      HB_TAG('O','C','I',' ')},       /* Occitan (post 1500) */
     372                 :   {"oj",      HB_TAG('O','J','B',' ')},       /* Ojibwa */
     373                 :   {"om",      HB_TAG('O','R','O',' ')},       /* Oromo */
     374                 :   {"or",      HB_TAG('O','R','I',' ')},       /* Oriya */
     375                 :   {"os",      HB_TAG('O','S','S',' ')},       /* Ossetian */
     376                 :   {"pa",      HB_TAG('P','A','N',' ')},       /* Panjabi */
     377                 :   {"pi",      HB_TAG('P','A','L',' ')},       /* Pali */
     378                 :   {"pl",      HB_TAG('P','L','K',' ')},       /* Polish */
     379                 :   {"plp",     HB_TAG('P','A','P',' ')},       /* Palpa */
     380                 :   {"prs",     HB_TAG('D','R','I',' ')},       /* Dari */
     381                 :   {"ps",      HB_TAG('P','A','S',' ')},       /* Pushto */
     382                 :   {"pt",      HB_TAG('P','T','G',' ')},       /* Portuguese */
     383                 :   {"raj",     HB_TAG('R','A','J',' ')},       /* Rajasthani */
     384                 :   {"ria",     HB_TAG('R','I','A',' ')},       /* Riang (India) */
     385                 :   {"ril",     HB_TAG('R','I','A',' ')},       /* Riang (Myanmar) */
     386                 :   {"ro",      HB_TAG('R','O','M',' ')},       /* Romanian */
     387                 :   {"rom",     HB_TAG('R','O','Y',' ')},       /* Romany */
     388                 :   {"ru",      HB_TAG('R','U','S',' ')},       /* Russian */
     389                 :   {"rue",     HB_TAG('R','S','Y',' ')},       /* Rusyn */
     390                 :   {"sa",      HB_TAG('S','A','N',' ')},       /* Sanskrit */
     391                 :   {"sah",     HB_TAG('Y','A','K',' ')},       /* Yakut */
     392                 :   {"sat",     HB_TAG('S','A','T',' ')},       /* Santali */
     393                 :   {"sck",     HB_TAG('S','A','D',' ')},       /* Sadri */
     394                 :   {"sd",      HB_TAG('S','N','D',' ')},       /* Sindhi */
     395                 :   {"se",      HB_TAG('N','S','M',' ')},       /* Northern Sami */
     396                 :   {"seh",     HB_TAG('S','N','A',' ')},       /* Sena */
     397                 :   {"sel",     HB_TAG('S','E','L',' ')},       /* Selkup */
     398                 :   {"sg",      HB_TAG('S','G','O',' ')},       /* Sango */
     399                 :   {"shn",     HB_TAG('S','H','N',' ')},       /* Shan */
     400                 :   {"si",      HB_TAG('S','N','H',' ')},       /* Sinhala */
     401                 :   {"sid",     HB_TAG('S','I','D',' ')},       /* Sidamo */
     402                 :   {"sjd",     HB_TAG('K','S','M',' ')},       /* Kildin Sami */
     403                 :   {"sk",      HB_TAG('S','K','Y',' ')},       /* Slovak */
     404                 :   {"skr",     HB_TAG('S','R','K',' ')},       /* Seraiki */
     405                 :   {"sl",      HB_TAG('S','L','V',' ')},       /* Slovenian */
     406                 :   {"sm",      HB_TAG('S','M','O',' ')},       /* Samoan */
     407                 :   {"sma",     HB_TAG('S','S','M',' ')},       /* Southern Sami */
     408                 :   {"smj",     HB_TAG('L','S','M',' ')},       /* Lule Sami */
     409                 :   {"smn",     HB_TAG('I','S','M',' ')},       /* Inari Sami */
     410                 :   {"sms",     HB_TAG('S','K','S',' ')},       /* Skolt Sami */
     411                 :   {"snk",     HB_TAG('S','N','K',' ')},       /* Soninke */
     412                 :   {"so",      HB_TAG('S','M','L',' ')},       /* Somali */
     413                 :   {"sq",      HB_TAG('S','Q','I',' ')},       /* Albanian */
     414                 :   {"sr",      HB_TAG('S','R','B',' ')},       /* Serbian */
     415                 :   {"srr",     HB_TAG('S','R','R',' ')},       /* Serer */
     416                 :   {"suq",     HB_TAG('S','U','R',' ')},       /* Suri */
     417                 :   {"sv",      HB_TAG('S','V','E',' ')},       /* Swedish */
     418                 :   {"sva",     HB_TAG('S','V','A',' ')},       /* Svan */
     419                 :   {"sw",      HB_TAG('S','W','K',' ')},       /* Swahili */
     420                 :   {"swb",     HB_TAG('C','M','R',' ')},       /* Comorian */
     421                 :   {"syr",     HB_TAG('S','Y','R',' ')},       /* Syriac */
     422                 :   {"ta",      HB_TAG('T','A','M',' ')},       /* Tamil */
     423                 :   {"tcy",     HB_TAG('T','U','L',' ')},       /* Tulu */
     424                 :   {"te",      HB_TAG('T','E','L',' ')},       /* Telugu */
     425                 :   {"tg",      HB_TAG('T','A','J',' ')},       /* Tajik */
     426                 :   {"th",      HB_TAG('T','H','A',' ')},       /* Thai */
     427                 :   {"ti",      HB_TAG('T','G','Y',' ')},       /* Tigrinya */
     428                 :   {"tig",     HB_TAG('T','G','R',' ')},       /* Tigre */
     429                 :   {"tk",      HB_TAG('T','K','M',' ')},       /* Turkmen */
     430                 :   {"tn",      HB_TAG('T','N','A',' ')},       /* Tswana */
     431                 :   {"tnz",     HB_TAG('T','N','G',' ')},       /* Tonga (Thailand) */
     432                 :   {"to",      HB_TAG('T','N','G',' ')},       /* Tonga (Tonga Islands) */
     433                 :   {"tog",     HB_TAG('T','N','G',' ')},       /* Tonga (Nyasa) */
     434                 :   {"toi",     HB_TAG('T','N','G',' ')},       /* Tonga (Zambia) */
     435                 :   {"tr",      HB_TAG('T','R','K',' ')},       /* Turkish */
     436                 :   {"ts",      HB_TAG('T','S','G',' ')},       /* Tsonga */
     437                 :   {"tt",      HB_TAG('T','A','T',' ')},       /* Tatar */
     438                 :   {"tw",      HB_TAG('T','W','I',' ')},       /* Twi */
     439                 :   {"ty",      HB_TAG('T','H','T',' ')},       /* Tahitian */
     440                 :   {"udm",     HB_TAG('U','D','M',' ')},       /* Udmurt */
     441                 :   {"ug",      HB_TAG('U','Y','G',' ')},       /* Uighur */
     442                 :   {"uk",      HB_TAG('U','K','R',' ')},       /* Ukrainian */
     443                 :   {"unr",     HB_TAG('M','U','N',' ')},       /* Mundari */
     444                 :   {"ur",      HB_TAG('U','R','D',' ')},       /* Urdu */
     445                 :   {"uz",      HB_TAG('U','Z','B',' ')},       /* Uzbek */
     446                 :   {"ve",      HB_TAG('V','E','N',' ')},       /* Venda */
     447                 :   {"vi",      HB_TAG('V','I','T',' ')},       /* Vietnamese */
     448                 :   {"wbm",     HB_TAG('W','A',' ',' ')},       /* Wa */
     449                 :   {"wbr",     HB_TAG('W','A','G',' ')},       /* Wagdi */
     450                 :   {"wo",      HB_TAG('W','L','F',' ')},       /* Wolof */
     451                 :   {"xal",     HB_TAG('K','L','M',' ')},       /* Kalmyk */
     452                 :   {"xh",      HB_TAG('X','H','S',' ')},       /* Xhosa */
     453                 :   {"xom",     HB_TAG('K','M','O',' ')},       /* Komo (Sudan) */
     454                 :   {"xsl",     HB_TAG('S','S','L',' ')},       /* South Slavey */
     455                 :   {"yi",      HB_TAG('J','I','I',' ')},       /* Yiddish */
     456                 :   {"yo",      HB_TAG('Y','B','A',' ')},       /* Yoruba */
     457                 :   {"yso",     HB_TAG('N','I','S',' ')},       /* Nisi (China) */
     458                 :   {"zne",     HB_TAG('Z','N','D',' ')},       /* Zande */
     459                 :   {"zu",      HB_TAG('Z','U','L',' ')}        /* Zulu */
     460                 : 
     461                 :   /* I couldn't find the language id for these */
     462                 : 
     463                 : /*{"??",      HB_TAG('A','G','W',' ')},*/     /* Agaw */
     464                 : /*{"??",      HB_TAG('A','L','S',' ')},*/     /* Alsatian */
     465                 : /*{"??",      HB_TAG('A','L','T',' ')},*/     /* Altai */
     466                 : /*{"??",      HB_TAG('A','R','K',' ')},*/     /* Arakanese */
     467                 : /*{"??",      HB_TAG('A','T','H',' ')},*/     /* Athapaskan */
     468                 : /*{"??",      HB_TAG('B','A','G',' ')},*/     /* Baghelkhandi */
     469                 : /*{"??",      HB_TAG('B','A','L',' ')},*/     /* Balkar */
     470                 : /*{"??",      HB_TAG('B','A','U',' ')},*/     /* Baule */
     471                 : /*{"??",      HB_TAG('B','B','R',' ')},*/     /* Berber */
     472                 : /*{"??",      HB_TAG('B','C','R',' ')},*/     /* Bible Cree */
     473                 : /*{"??",      HB_TAG('B','E','L',' ')},*/     /* Belarussian */
     474                 : /*{"??",      HB_TAG('B','I','L',' ')},*/     /* Bilen */
     475                 : /*{"??",      HB_TAG('B','K','F',' ')},*/     /* Blackfoot */
     476                 : /*{"??",      HB_TAG('B','L','N',' ')},*/     /* Balante */
     477                 : /*{"??",      HB_TAG('B','M','L',' ')},*/     /* Bamileke */
     478                 : /*{"??",      HB_TAG('B','R','I',' ')},*/     /* Braj Bhasha */
     479                 : /*{"??",      HB_TAG('C','H','G',' ')},*/     /* Chaha Gurage */
     480                 : /*{"??",      HB_TAG('C','H','H',' ')},*/     /* Chattisgarhi */
     481                 : /*{"??",      HB_TAG('C','H','K',' ')},*/     /* Chukchi */
     482                 : /*{"??",      HB_TAG('D','J','R',' ')},*/     /* Djerma */
     483                 : /*{"??",      HB_TAG('D','N','G',' ')},*/     /* Dangme */
     484                 : /*{"??",      HB_TAG('E','C','R',' ')},*/     /* Eastern Cree */
     485                 : /*{"??",      HB_TAG('F','A','N',' ')},*/     /* French Antillean */
     486                 : /*{"??",      HB_TAG('F','L','E',' ')},*/     /* Flemish */
     487                 : /*{"??",      HB_TAG('F','N','E',' ')},*/     /* Forest Nenets */
     488                 : /*{"??",      HB_TAG('F','T','A',' ')},*/     /* Futa */
     489                 : /*{"??",      HB_TAG('G','A','R',' ')},*/     /* Garshuni */
     490                 : /*{"??",      HB_TAG('G','E','Z',' ')},*/     /* Ge'ez */
     491                 : /*{"??",      HB_TAG('H','A','L',' ')},*/     /* Halam */
     492                 : /*{"??",      HB_TAG('H','A','R',' ')},*/     /* Harauti */
     493                 : /*{"??",      HB_TAG('H','A','W',' ')},*/     /* Hawaiin */
     494                 : /*{"??",      HB_TAG('H','B','N',' ')},*/     /* Hammer-Banna */
     495                 : /*{"??",      HB_TAG('H','M','A',' ')},*/     /* High Mari */
     496                 : /*{"??",      HB_TAG('H','N','D',' ')},*/     /* Hindko */
     497                 : /*{"??",      HB_TAG('I','J','O',' ')},*/     /* Ijo */
     498                 : /*{"??",      HB_TAG('I','L','O',' ')},*/     /* Ilokano */
     499                 : /*{"??",      HB_TAG('I','R','T',' ')},*/     /* Irish Traditional */
     500                 : /*{"??",      HB_TAG('J','U','L',' ')},*/     /* Jula */
     501                 : /*{"??",      HB_TAG('K','A','R',' ')},*/     /* Karachay */
     502                 : /*{"??",      HB_TAG('K','E','B',' ')},*/     /* Kebena */
     503                 : /*{"??",      HB_TAG('K','G','E',' ')},*/     /* Khutsuri Georgian */
     504                 : /*{"??",      HB_TAG('K','H','A',' ')},*/     /* Khakass */
     505                 : /*{"??",      HB_TAG('K','H','K',' ')},*/     /* Khanty-Kazim */
     506                 : /*{"??",      HB_TAG('K','H','S',' ')},*/     /* Khanty-Shurishkar */
     507                 : /*{"??",      HB_TAG('K','H','V',' ')},*/     /* Khanty-Vakhi */
     508                 : /*{"??",      HB_TAG('K','I','S',' ')},*/     /* Kisii */
     509                 : /*{"??",      HB_TAG('K','K','N',' ')},*/     /* Kokni */
     510                 : /*{"??",      HB_TAG('K','M','S',' ')},*/     /* Komso */
     511                 : /*{"??",      HB_TAG('K','O','D',' ')},*/     /* Kodagu */
     512                 : /*{"??",      HB_TAG('K','O','H',' ')},*/     /* Korean Old Hangul */
     513                 : /*{"??",      HB_TAG('K','O','N',' ')},*/     /* Kikongo */
     514                 : /*{"??",      HB_TAG('K','R','K',' ')},*/     /* Karakalpak */
     515                 : /*{"??",      HB_TAG('K','R','N',' ')},*/     /* Karen */
     516                 : /*{"??",      HB_TAG('K','U','L',' ')},*/     /* Kulvi */
     517                 : /*{"??",      HB_TAG('L','A','H',' ')},*/     /* Lahuli */
     518                 : /*{"??",      HB_TAG('L','A','M',' ')},*/     /* Lambani */
     519                 : /*{"??",      HB_TAG('L','C','R',' ')},*/     /* L-Cree */
     520                 : /*{"??",      HB_TAG('L','E','Z',' ')},*/     /* Lezgi */
     521                 : /*{"??",      HB_TAG('L','M','A',' ')},*/     /* Low Mari */
     522                 : /*{"??",      HB_TAG('L','U','B',' ')},*/     /* Luba */
     523                 : /*{"??",      HB_TAG('L','U','G',' ')},*/     /* Luganda */
     524                 : /*{"??",      HB_TAG('L','U','H',' ')},*/     /* Luhya */
     525                 : /*{"??",      HB_TAG('M','A','K',' ')},*/     /* Makua */
     526                 : /*{"??",      HB_TAG('M','A','L',' ')},*/     /* Malayalam Traditional */
     527                 : /*{"??",      HB_TAG('M','B','N',' ')},*/     /* Mbundu */
     528                 : /*{"??",      HB_TAG('M','I','Z',' ')},*/     /* Mizo */
     529                 : /*{"??",      HB_TAG('M','L','N',' ')},*/     /* Malinke */
     530                 : /*{"??",      HB_TAG('M','N','K',' ')},*/     /* Maninka */
     531                 : /*{"??",      HB_TAG('M','O','R',' ')},*/     /* Moroccan */
     532                 : /*{"??",      HB_TAG('N','A','G',' ')},*/     /* Naga-Assamese */
     533                 : /*{"??",      HB_TAG('N','C','R',' ')},*/     /* N-Cree */
     534                 : /*{"??",      HB_TAG('N','D','B',' ')},*/     /* Ndebele */
     535                 : /*{"??",      HB_TAG('N','G','R',' ')},*/     /* Nagari */
     536                 : /*{"??",      HB_TAG('N','H','C',' ')},*/     /* Norway House Cree */
     537                 : /*{"??",      HB_TAG('N','K','L',' ')},*/     /* Nkole */
     538                 : /*{"??",      HB_TAG('N','T','A',' ')},*/     /* Northern Tai */
     539                 : /*{"??",      HB_TAG('O','C','R',' ')},*/     /* Oji-Cree */
     540                 : /*{"??",      HB_TAG('P','A','A',' ')},*/     /* Palestinian Aramaic */
     541                 : /*{"??",      HB_TAG('P','G','R',' ')},*/     /* Polytonic Greek */
     542                 : /*{"??",      HB_TAG('P','L','G',' ')},*/     /* Palaung */
     543                 : /*{"??",      HB_TAG('Q','I','N',' ')},*/     /* Chin */
     544                 : /*{"??",      HB_TAG('R','B','U',' ')},*/     /* Russian Buriat */
     545                 : /*{"??",      HB_TAG('R','C','R',' ')},*/     /* R-Cree */
     546                 : /*{"??",      HB_TAG('R','M','S',' ')},*/     /* Rhaeto-Romanic */
     547                 : /*{"??",      HB_TAG('R','U','A',' ')},*/     /* Ruanda */
     548                 : /*{"??",      HB_TAG('S','A','Y',' ')},*/     /* Sayisi */
     549                 : /*{"??",      HB_TAG('S','E','K',' ')},*/     /* Sekota */
     550                 : /*{"??",      HB_TAG('S','I','G',' ')},*/     /* Silte Gurage */
     551                 : /*{"??",      HB_TAG('S','L','A',' ')},*/     /* Slavey */
     552                 : /*{"??",      HB_TAG('S','O','G',' ')},*/     /* Sodo Gurage */
     553                 : /*{"??",      HB_TAG('S','O','T',' ')},*/     /* Sotho */
     554                 : /*{"??",      HB_TAG('S','W','A',' ')},*/     /* Swadaya Aramaic */
     555                 : /*{"??",      HB_TAG('S','W','Z',' ')},*/     /* Swazi */
     556                 : /*{"??",      HB_TAG('S','X','T',' ')},*/     /* Sutu */
     557                 : /*{"??",      HB_TAG('T','A','B',' ')},*/     /* Tabasaran */
     558                 : /*{"??",      HB_TAG('T','C','R',' ')},*/     /* TH-Cree */
     559                 : /*{"??",      HB_TAG('T','G','N',' ')},*/     /* Tongan */
     560                 : /*{"??",      HB_TAG('T','M','N',' ')},*/     /* Temne */
     561                 : /*{"??",      HB_TAG('T','N','E',' ')},*/     /* Tundra Nenets */
     562                 : /*{"??",      HB_TAG('T','O','D',' ')},*/     /* Todo */
     563                 : /*{"??",      HB_TAG('T','U','A',' ')},*/     /* Turoyo Aramaic */
     564                 : /*{"??",      HB_TAG('T','U','V',' ')},*/     /* Tuvin */
     565                 : /*{"??",      HB_TAG('W','C','R',' ')},*/     /* West-Cree */
     566                 : /*{"??",      HB_TAG('X','B','D',' ')},*/     /* Tai Lue */
     567                 : /*{"??",      HB_TAG('Y','C','R',' ')},*/     /* Y-Cree */
     568                 : /*{"??",      HB_TAG('Y','I','C',' ')},*/     /* Yi Classic */
     569                 : /*{"??",      HB_TAG('Y','I','M',' ')},*/     /* Yi Modern */
     570                 : /*{"??",      HB_TAG('Z','H','P',' ')},*/     /* Chinese Phonetic */
     571                 : };
     572                 : 
     573                 : static const LangTag ot_languages_zh[] = {
     574                 :   {"zh-cn",   HB_TAG('Z','H','S',' ')},       /* Chinese (China) */
     575                 :   {"zh-hk",   HB_TAG('Z','H','H',' ')},       /* Chinese (Hong Kong) */
     576                 :   {"zh-mo",   HB_TAG('Z','H','T',' ')},       /* Chinese (Macao) */
     577                 :   {"zh-sg",   HB_TAG('Z','H','S',' ')},       /* Chinese (Singapore) */
     578                 :   {"zh-tw",   HB_TAG('Z','H','T',' ')}        /* Chinese (Taiwan) */
     579                 : };
     580                 : 
     581                 : static int
     582               0 : lang_compare_first_component (const char *a,
     583                 :                               const char *b)
     584                 : {
     585                 :   unsigned int da, db;
     586                 :   const char *p;
     587                 : 
     588               0 :   p = strchr (a, '-');
     589               0 :   da = p ? (unsigned int) (p - a) : strlen (a);
     590                 : 
     591               0 :   p = strchr (b, '-');
     592               0 :   db = p ? (unsigned int) (p - b) : strlen (b);
     593                 : 
     594               0 :   return strncmp (a, b, MAX (da, db));
     595                 : }
     596                 : 
     597                 : static hb_bool_t
     598               0 : lang_matches (const char *lang_str, const char *spec)
     599                 : {
     600               0 :   unsigned int len = strlen (spec);
     601                 : 
     602               0 :   return strncmp (lang_str, spec, len) == 0 &&
     603               0 :          (lang_str[len] == '\0' || lang_str[len] == '-');
     604                 : }
     605                 : 
     606                 : hb_tag_t
     607               0 : hb_ot_tag_from_language (hb_language_t language)
     608                 : {
     609                 :   const char *lang_str, *s;
     610                 :   const LangTag *lang_tag;
     611                 : 
     612               0 :   if (language == HB_LANGUAGE_INVALID)
     613               0 :     return HB_OT_TAG_DEFAULT_LANGUAGE;
     614                 : 
     615               0 :   lang_str = hb_language_to_string (language);
     616                 : 
     617               0 :   s = strstr (lang_str, "x-hbot");
     618               0 :   if (s) {
     619                 :     char tag[4];
     620                 :     int i;
     621               0 :     s += 6;
     622               0 :     for (i = 0; i < 4 && ISALPHA (s[i]); i++)
     623               0 :       tag[i] = TOUPPER (s[i]);
     624               0 :     if (i) {
     625               0 :       for (; i < 4; i++)
     626               0 :         tag[i] = ' ';
     627               0 :       return HB_TAG_CHAR4 (tag);
     628                 :     }
     629                 :   }
     630                 : 
     631                 :   /* Find a language matching in the first component */
     632                 :   lang_tag = (LangTag *) bsearch (lang_str, ot_languages,
     633                 :                                   ARRAY_LENGTH (ot_languages), sizeof (LangTag),
     634               0 :                                   (hb_compare_func_t) lang_compare_first_component);
     635               0 :   if (lang_tag)
     636               0 :     return lang_tag->tag;
     637                 : 
     638                 :   /* Otherwise, check the Chinese ones */
     639               0 :   if (0 == lang_compare_first_component (lang_str, "zh"))
     640                 :   {
     641                 :     unsigned int i;
     642                 : 
     643               0 :     for (i = 0; i < ARRAY_LENGTH (ot_languages_zh); i++)
     644                 :     {
     645               0 :       lang_tag = &ot_languages_zh[i];
     646               0 :       if (lang_matches (lang_tag->language, lang_str))
     647               0 :         return lang_tag->tag;
     648                 :     }
     649                 : 
     650                 :     /* Otherwise just return 'ZHS ' */
     651               0 :     return HB_TAG('Z','H','S',' ');
     652                 :   }
     653                 : 
     654               0 :   s = strchr (lang_str, '-');
     655               0 :   if (!s)
     656               0 :     s = lang_str + strlen (lang_str);
     657               0 :   if (s - lang_str == 3) {
     658                 :     /* Assume it's ISO-639-3 and upper-case and use it. */
     659               0 :     return hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000;
     660                 :   }
     661                 : 
     662               0 :   return HB_OT_TAG_DEFAULT_LANGUAGE;
     663                 : }
     664                 : 
     665                 : hb_language_t
     666               0 : hb_ot_tag_to_language (hb_tag_t tag)
     667                 : {
     668                 :   unsigned int i;
     669                 : 
     670               0 :   if (tag == HB_OT_TAG_DEFAULT_LANGUAGE)
     671               0 :     return NULL;
     672                 : 
     673               0 :   for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
     674               0 :     if (ot_languages[i].tag == tag)
     675               0 :       return hb_language_from_string (ot_languages[i].language, -1);
     676                 : 
     677                 :   /* If tag starts with ZH, it's Chinese */
     678               0 :   if ((tag & 0xFFFF0000)  == 0x5A480000) {
     679               0 :     switch (tag) {
     680               0 :       case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk", -1); /* Hong Kong */
     681                 :       default: {
     682                 :         /* Encode the tag... */
     683               0 :         unsigned char buf[14] = "zh-x-hbot";
     684               0 :         buf[9] = tag >> 24;
     685               0 :         buf[10] = (tag >> 16) & 0xFF;
     686               0 :         buf[11] = (tag >> 8) & 0xFF;
     687               0 :         buf[12] = tag & 0xFF;
     688               0 :         if (buf[12] == 0x20)
     689               0 :           buf[12] = '\0';
     690               0 :         buf[13] = '\0';
     691               0 :         return hb_language_from_string ((char *) buf, -1);
     692                 :       }
     693                 :     }
     694                 :   }
     695                 : 
     696                 :   /* Else return a custom language in the form of "x-hbotABCD" */
     697                 :   {
     698               0 :     unsigned char buf[11] = "x-hbot";
     699               0 :     buf[6] = tag >> 24;
     700               0 :     buf[7] = (tag >> 16) & 0xFF;
     701               0 :     buf[8] = (tag >> 8) & 0xFF;
     702               0 :     buf[9] = tag & 0xFF;
     703               0 :     if (buf[9] == 0x20)
     704               0 :       buf[9] = '\0';
     705               0 :     buf[10] = '\0';
     706               0 :     return hb_language_from_string ((char *) buf, -1);
     707                 :   }
     708                 : }
     709                 : 
     710                 : 

Generated by: LCOV version 1.7