| 1 | /**************************************************************************** | 
|---|
| 2 | ** | 
|---|
| 3 | ** Copyright (C) 2020 The Qt Company Ltd. | 
|---|
| 4 | ** Copyright (C) 2013 Konstantin Ritt | 
|---|
| 5 | ** Contact: https://www.qt.io/licensing/ | 
|---|
| 6 | ** | 
|---|
| 7 | ** This file is part of the QtGui module of the Qt Toolkit. | 
|---|
| 8 | ** | 
|---|
| 9 | ** $QT_BEGIN_LICENSE:LGPL$ | 
|---|
| 10 | ** Commercial License Usage | 
|---|
| 11 | ** Licensees holding valid commercial Qt licenses may use this file in | 
|---|
| 12 | ** accordance with the commercial license agreement provided with the | 
|---|
| 13 | ** Software or, alternatively, in accordance with the terms contained in | 
|---|
| 14 | ** a written agreement between you and The Qt Company. For licensing terms | 
|---|
| 15 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
|---|
| 16 | ** information use the contact form at https://www.qt.io/contact-us. | 
|---|
| 17 | ** | 
|---|
| 18 | ** GNU Lesser General Public License Usage | 
|---|
| 19 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
|---|
| 20 | ** General Public License version 3 as published by the Free Software | 
|---|
| 21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
|---|
| 22 | ** packaging of this file. Please review the following information to | 
|---|
| 23 | ** ensure the GNU Lesser General Public License version 3 requirements | 
|---|
| 24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
|---|
| 25 | ** | 
|---|
| 26 | ** GNU General Public License Usage | 
|---|
| 27 | ** Alternatively, this file may be used under the terms of the GNU | 
|---|
| 28 | ** General Public License version 2.0 or (at your option) the GNU General | 
|---|
| 29 | ** Public license version 3 or any later version approved by the KDE Free | 
|---|
| 30 | ** Qt Foundation. The licenses are as published by the Free Software | 
|---|
| 31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
|---|
| 32 | ** included in the packaging of this file. Please review the following | 
|---|
| 33 | ** information to ensure the GNU General Public License requirements will | 
|---|
| 34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
|---|
| 35 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
|---|
| 36 | ** | 
|---|
| 37 | ** $QT_END_LICENSE$ | 
|---|
| 38 | ** | 
|---|
| 39 | ****************************************************************************/ | 
|---|
| 40 |  | 
|---|
| 41 | #include "qharfbuzzng_p.h" | 
|---|
| 42 |  | 
|---|
| 43 | #include <qstring.h> | 
|---|
| 44 |  | 
|---|
| 45 | #include <private/qstringiterator_p.h> | 
|---|
| 46 |  | 
|---|
| 47 | #include "qfontengine_p.h" | 
|---|
| 48 |  | 
|---|
| 49 | QT_BEGIN_NAMESPACE | 
|---|
| 50 |  | 
|---|
| 51 | // Unicode routines | 
|---|
| 52 |  | 
|---|
| 53 | static const hb_script_t _qtscript_to_hbscript[] = { | 
|---|
| 54 | HB_SCRIPT_UNKNOWN, | 
|---|
| 55 | HB_SCRIPT_INHERITED, | 
|---|
| 56 | HB_SCRIPT_COMMON, | 
|---|
| 57 |  | 
|---|
| 58 | HB_SCRIPT_LATIN, | 
|---|
| 59 | HB_SCRIPT_GREEK, | 
|---|
| 60 | HB_SCRIPT_CYRILLIC, | 
|---|
| 61 | HB_SCRIPT_ARMENIAN, | 
|---|
| 62 | HB_SCRIPT_HEBREW, | 
|---|
| 63 | HB_SCRIPT_ARABIC, | 
|---|
| 64 | HB_SCRIPT_SYRIAC, | 
|---|
| 65 | HB_SCRIPT_THAANA, | 
|---|
| 66 | HB_SCRIPT_DEVANAGARI, | 
|---|
| 67 | HB_SCRIPT_BENGALI, | 
|---|
| 68 | HB_SCRIPT_GURMUKHI, | 
|---|
| 69 | HB_SCRIPT_GUJARATI, | 
|---|
| 70 | HB_SCRIPT_ORIYA, | 
|---|
| 71 | HB_SCRIPT_TAMIL, | 
|---|
| 72 | HB_SCRIPT_TELUGU, | 
|---|
| 73 | HB_SCRIPT_KANNADA, | 
|---|
| 74 | HB_SCRIPT_MALAYALAM, | 
|---|
| 75 | HB_SCRIPT_SINHALA, | 
|---|
| 76 | HB_SCRIPT_THAI, | 
|---|
| 77 | HB_SCRIPT_LAO, | 
|---|
| 78 | HB_SCRIPT_TIBETAN, | 
|---|
| 79 | HB_SCRIPT_MYANMAR, | 
|---|
| 80 | HB_SCRIPT_GEORGIAN, | 
|---|
| 81 | HB_SCRIPT_HANGUL, | 
|---|
| 82 | HB_SCRIPT_ETHIOPIC, | 
|---|
| 83 | HB_SCRIPT_CHEROKEE, | 
|---|
| 84 | HB_SCRIPT_CANADIAN_SYLLABICS, | 
|---|
| 85 | HB_SCRIPT_OGHAM, | 
|---|
| 86 | HB_SCRIPT_RUNIC, | 
|---|
| 87 | HB_SCRIPT_KHMER, | 
|---|
| 88 | HB_SCRIPT_MONGOLIAN, | 
|---|
| 89 | HB_SCRIPT_HIRAGANA, | 
|---|
| 90 | HB_SCRIPT_KATAKANA, | 
|---|
| 91 | HB_SCRIPT_BOPOMOFO, | 
|---|
| 92 | HB_SCRIPT_HAN, | 
|---|
| 93 | HB_SCRIPT_YI, | 
|---|
| 94 | HB_SCRIPT_OLD_ITALIC, | 
|---|
| 95 | HB_SCRIPT_GOTHIC, | 
|---|
| 96 | HB_SCRIPT_DESERET, | 
|---|
| 97 | HB_SCRIPT_TAGALOG, | 
|---|
| 98 | HB_SCRIPT_HANUNOO, | 
|---|
| 99 | HB_SCRIPT_BUHID, | 
|---|
| 100 | HB_SCRIPT_TAGBANWA, | 
|---|
| 101 | HB_SCRIPT_COPTIC, | 
|---|
| 102 |  | 
|---|
| 103 | // Unicode 4.0 additions | 
|---|
| 104 | HB_SCRIPT_LIMBU, | 
|---|
| 105 | HB_SCRIPT_TAI_LE, | 
|---|
| 106 | HB_SCRIPT_LINEAR_B, | 
|---|
| 107 | HB_SCRIPT_UGARITIC, | 
|---|
| 108 | HB_SCRIPT_SHAVIAN, | 
|---|
| 109 | HB_SCRIPT_OSMANYA, | 
|---|
| 110 | HB_SCRIPT_CYPRIOT, | 
|---|
| 111 | HB_SCRIPT_BRAILLE, | 
|---|
| 112 |  | 
|---|
| 113 | // Unicode 4.1 additions | 
|---|
| 114 | HB_SCRIPT_BUGINESE, | 
|---|
| 115 | HB_SCRIPT_NEW_TAI_LUE, | 
|---|
| 116 | HB_SCRIPT_GLAGOLITIC, | 
|---|
| 117 | HB_SCRIPT_TIFINAGH, | 
|---|
| 118 | HB_SCRIPT_SYLOTI_NAGRI, | 
|---|
| 119 | HB_SCRIPT_OLD_PERSIAN, | 
|---|
| 120 | HB_SCRIPT_KHAROSHTHI, | 
|---|
| 121 |  | 
|---|
| 122 | // Unicode 5.0 additions | 
|---|
| 123 | HB_SCRIPT_BALINESE, | 
|---|
| 124 | HB_SCRIPT_CUNEIFORM, | 
|---|
| 125 | HB_SCRIPT_PHOENICIAN, | 
|---|
| 126 | HB_SCRIPT_PHAGS_PA, | 
|---|
| 127 | HB_SCRIPT_NKO, | 
|---|
| 128 |  | 
|---|
| 129 | // Unicode 5.1 additions | 
|---|
| 130 | HB_SCRIPT_SUNDANESE, | 
|---|
| 131 | HB_SCRIPT_LEPCHA, | 
|---|
| 132 | HB_SCRIPT_OL_CHIKI, | 
|---|
| 133 | HB_SCRIPT_VAI, | 
|---|
| 134 | HB_SCRIPT_SAURASHTRA, | 
|---|
| 135 | HB_SCRIPT_KAYAH_LI, | 
|---|
| 136 | HB_SCRIPT_REJANG, | 
|---|
| 137 | HB_SCRIPT_LYCIAN, | 
|---|
| 138 | HB_SCRIPT_CARIAN, | 
|---|
| 139 | HB_SCRIPT_LYDIAN, | 
|---|
| 140 | HB_SCRIPT_CHAM, | 
|---|
| 141 |  | 
|---|
| 142 | // Unicode 5.2 additions | 
|---|
| 143 | HB_SCRIPT_TAI_THAM, | 
|---|
| 144 | HB_SCRIPT_TAI_VIET, | 
|---|
| 145 | HB_SCRIPT_AVESTAN, | 
|---|
| 146 | HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, | 
|---|
| 147 | HB_SCRIPT_SAMARITAN, | 
|---|
| 148 | HB_SCRIPT_LISU, | 
|---|
| 149 | HB_SCRIPT_BAMUM, | 
|---|
| 150 | HB_SCRIPT_JAVANESE, | 
|---|
| 151 | HB_SCRIPT_MEETEI_MAYEK, | 
|---|
| 152 | HB_SCRIPT_IMPERIAL_ARAMAIC, | 
|---|
| 153 | HB_SCRIPT_OLD_SOUTH_ARABIAN, | 
|---|
| 154 | HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, | 
|---|
| 155 | HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, | 
|---|
| 156 | HB_SCRIPT_OLD_TURKIC, | 
|---|
| 157 | HB_SCRIPT_KAITHI, | 
|---|
| 158 |  | 
|---|
| 159 | // Unicode 6.0 additions | 
|---|
| 160 | HB_SCRIPT_BATAK, | 
|---|
| 161 | HB_SCRIPT_BRAHMI, | 
|---|
| 162 | HB_SCRIPT_MANDAIC, | 
|---|
| 163 |  | 
|---|
| 164 | // Unicode 6.1 additions | 
|---|
| 165 | HB_SCRIPT_CHAKMA, | 
|---|
| 166 | HB_SCRIPT_MEROITIC_CURSIVE, | 
|---|
| 167 | HB_SCRIPT_MEROITIC_HIEROGLYPHS, | 
|---|
| 168 | HB_SCRIPT_MIAO, | 
|---|
| 169 | HB_SCRIPT_SHARADA, | 
|---|
| 170 | HB_SCRIPT_SORA_SOMPENG, | 
|---|
| 171 | HB_SCRIPT_TAKRI, | 
|---|
| 172 |  | 
|---|
| 173 | // Unicode 7.0 additions | 
|---|
| 174 | HB_SCRIPT_CAUCASIAN_ALBANIAN, | 
|---|
| 175 | HB_SCRIPT_BASSA_VAH, | 
|---|
| 176 | HB_SCRIPT_DUPLOYAN, | 
|---|
| 177 | HB_SCRIPT_ELBASAN, | 
|---|
| 178 | HB_SCRIPT_GRANTHA, | 
|---|
| 179 | HB_SCRIPT_PAHAWH_HMONG, | 
|---|
| 180 | HB_SCRIPT_KHOJKI, | 
|---|
| 181 | HB_SCRIPT_LINEAR_A, | 
|---|
| 182 | HB_SCRIPT_MAHAJANI, | 
|---|
| 183 | HB_SCRIPT_MANICHAEAN, | 
|---|
| 184 | HB_SCRIPT_MENDE_KIKAKUI, | 
|---|
| 185 | HB_SCRIPT_MODI, | 
|---|
| 186 | HB_SCRIPT_MRO, | 
|---|
| 187 | HB_SCRIPT_OLD_NORTH_ARABIAN, | 
|---|
| 188 | HB_SCRIPT_NABATAEAN, | 
|---|
| 189 | HB_SCRIPT_PALMYRENE, | 
|---|
| 190 | HB_SCRIPT_PAU_CIN_HAU, | 
|---|
| 191 | HB_SCRIPT_OLD_PERMIC, | 
|---|
| 192 | HB_SCRIPT_PSALTER_PAHLAVI, | 
|---|
| 193 | HB_SCRIPT_SIDDHAM, | 
|---|
| 194 | HB_SCRIPT_KHUDAWADI, | 
|---|
| 195 | HB_SCRIPT_TIRHUTA, | 
|---|
| 196 | HB_SCRIPT_WARANG_CITI, | 
|---|
| 197 |  | 
|---|
| 198 | // Unicode 8.0 additions | 
|---|
| 199 | HB_SCRIPT_AHOM, | 
|---|
| 200 | HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, | 
|---|
| 201 | HB_SCRIPT_HATRAN, | 
|---|
| 202 | HB_SCRIPT_MULTANI, | 
|---|
| 203 | HB_SCRIPT_OLD_HUNGARIAN, | 
|---|
| 204 | HB_SCRIPT_SIGNWRITING, | 
|---|
| 205 |  | 
|---|
| 206 | // Unicode 9.0 additions | 
|---|
| 207 | HB_SCRIPT_ADLAM, | 
|---|
| 208 | HB_SCRIPT_BHAIKSUKI, | 
|---|
| 209 | HB_SCRIPT_MARCHEN, | 
|---|
| 210 | HB_SCRIPT_NEWA, | 
|---|
| 211 | HB_SCRIPT_OSAGE, | 
|---|
| 212 | HB_SCRIPT_TANGUT, | 
|---|
| 213 |  | 
|---|
| 214 | // Unicode 10.0 additions | 
|---|
| 215 | HB_SCRIPT_MASARAM_GONDI, | 
|---|
| 216 | HB_SCRIPT_NUSHU, | 
|---|
| 217 | HB_SCRIPT_SOYOMBO, | 
|---|
| 218 | HB_SCRIPT_ZANABAZAR_SQUARE, | 
|---|
| 219 |  | 
|---|
| 220 | // Unicode 11.0 additions | 
|---|
| 221 | HB_SCRIPT_DOGRA, | 
|---|
| 222 | HB_SCRIPT_GUNJALA_GONDI, | 
|---|
| 223 | HB_SCRIPT_HANIFI_ROHINGYA, | 
|---|
| 224 | HB_SCRIPT_MAKASAR, | 
|---|
| 225 | HB_SCRIPT_MEDEFAIDRIN, | 
|---|
| 226 | HB_SCRIPT_OLD_SOGDIAN, | 
|---|
| 227 | HB_SCRIPT_SOGDIAN, | 
|---|
| 228 |  | 
|---|
| 229 | // Unicode 12.0 additions | 
|---|
| 230 | HB_SCRIPT_ELYMAIC, | 
|---|
| 231 | HB_SCRIPT_NANDINAGARI, | 
|---|
| 232 | HB_SCRIPT_NYIAKENG_PUACHUE_HMONG, | 
|---|
| 233 | HB_SCRIPT_WANCHO, | 
|---|
| 234 |  | 
|---|
| 235 | // Unicode 13.0 additions (not present in harfbuzz-ng 2.6.4) | 
|---|
| 236 | hb_script_t(HB_TAG('C', 'h', 'o', 'r')), // Script_Chorasmian | 
|---|
| 237 | hb_script_t(HB_TAG('D', 'i', 'v', 'e')), // Script_DivesAkuru | 
|---|
| 238 | hb_script_t(HB_TAG('K', 'h', 'i', 't')), // Script_KhitanSmallScript | 
|---|
| 239 | hb_script_t(HB_TAG('Y', 'e', 'z', 'i')), // Script_Yezidi | 
|---|
| 240 | }; | 
|---|
| 241 | static_assert(QChar::ScriptCount == sizeof(_qtscript_to_hbscript) / sizeof(_qtscript_to_hbscript[0])); | 
|---|
| 242 |  | 
|---|
| 243 | hb_script_t hb_qt_script_to_script(QChar::Script script) | 
|---|
| 244 | { | 
|---|
| 245 | return _qtscript_to_hbscript[script]; | 
|---|
| 246 | } | 
|---|
| 247 |  | 
|---|
| 248 | QChar::Script hb_qt_script_from_script(hb_script_t script) | 
|---|
| 249 | { | 
|---|
| 250 | uint i = QChar::ScriptCount - 1; | 
|---|
| 251 | while (i > QChar::Script_Unknown && _qtscript_to_hbscript[i] != script) | 
|---|
| 252 | --i; | 
|---|
| 253 | return QChar::Script(i); | 
|---|
| 254 | } | 
|---|
| 255 |  | 
|---|
| 256 |  | 
|---|
| 257 | static hb_unicode_combining_class_t | 
|---|
| 258 | _hb_qt_unicode_combining_class(hb_unicode_funcs_t * /*ufuncs*/, | 
|---|
| 259 | hb_codepoint_t unicode, | 
|---|
| 260 | void * /*user_data*/) | 
|---|
| 261 | { | 
|---|
| 262 | return hb_unicode_combining_class_t(QChar::combiningClass(unicode)); | 
|---|
| 263 | } | 
|---|
| 264 |  | 
|---|
| 265 | static const hb_unicode_general_category_t _qtcategory_to_hbcategory[] = { | 
|---|
| 266 | HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK,    //   Mn | 
|---|
| 267 | HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK,        //   Mc | 
|---|
| 268 | HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK,      //   Me | 
|---|
| 269 |  | 
|---|
| 270 | HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER,      //   Nd | 
|---|
| 271 | HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER,       //   Nl | 
|---|
| 272 | HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER,        //   No | 
|---|
| 273 |  | 
|---|
| 274 | HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR,     //   Zs | 
|---|
| 275 | HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR,      //   Zl | 
|---|
| 276 | HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR, //   Zp | 
|---|
| 277 |  | 
|---|
| 278 | HB_UNICODE_GENERAL_CATEGORY_CONTROL,             //   Cc | 
|---|
| 279 | HB_UNICODE_GENERAL_CATEGORY_FORMAT,              //   Cf | 
|---|
| 280 | HB_UNICODE_GENERAL_CATEGORY_SURROGATE,           //   Cs | 
|---|
| 281 | HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE,         //   Co | 
|---|
| 282 | HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED,          //   Cn | 
|---|
| 283 |  | 
|---|
| 284 | HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER,    //   Lu | 
|---|
| 285 | HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER,    //   Ll | 
|---|
| 286 | HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER,    //   Lt | 
|---|
| 287 | HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER,     //   Lm | 
|---|
| 288 | HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER,        //   Lo | 
|---|
| 289 |  | 
|---|
| 290 | HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION, //   Pc | 
|---|
| 291 | HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION,    //   Pd | 
|---|
| 292 | HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION,    //   Ps | 
|---|
| 293 | HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION,   //   Pe | 
|---|
| 294 | HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION, //   Pi | 
|---|
| 295 | HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION,   //   Pf | 
|---|
| 296 | HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION,   //   Po | 
|---|
| 297 |  | 
|---|
| 298 | HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL,         //   Sm | 
|---|
| 299 | HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL,     //   Sc | 
|---|
| 300 | HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL,     //   Sk | 
|---|
| 301 | HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL         //   So | 
|---|
| 302 | }; | 
|---|
| 303 |  | 
|---|
| 304 | static hb_unicode_general_category_t | 
|---|
| 305 | _hb_qt_unicode_general_category(hb_unicode_funcs_t * /*ufuncs*/, | 
|---|
| 306 | hb_codepoint_t unicode, | 
|---|
| 307 | void * /*user_data*/) | 
|---|
| 308 | { | 
|---|
| 309 | return _qtcategory_to_hbcategory[QChar::category(unicode)]; | 
|---|
| 310 | } | 
|---|
| 311 |  | 
|---|
| 312 | static hb_codepoint_t | 
|---|
| 313 | _hb_qt_unicode_mirroring(hb_unicode_funcs_t * /*ufuncs*/, | 
|---|
| 314 | hb_codepoint_t unicode, | 
|---|
| 315 | void * /*user_data*/) | 
|---|
| 316 | { | 
|---|
| 317 | return QChar::mirroredChar(unicode); | 
|---|
| 318 | } | 
|---|
| 319 |  | 
|---|
| 320 | static hb_script_t | 
|---|
| 321 | _hb_qt_unicode_script(hb_unicode_funcs_t * /*ufuncs*/, | 
|---|
| 322 | hb_codepoint_t unicode, | 
|---|
| 323 | void * /*user_data*/) | 
|---|
| 324 | { | 
|---|
| 325 | return _qtscript_to_hbscript[QChar::script(unicode)]; | 
|---|
| 326 | } | 
|---|
| 327 |  | 
|---|
| 328 | static hb_bool_t | 
|---|
| 329 | _hb_qt_unicode_compose(hb_unicode_funcs_t * /*ufuncs*/, | 
|---|
| 330 | hb_codepoint_t a, hb_codepoint_t b, | 
|---|
| 331 | hb_codepoint_t *ab, | 
|---|
| 332 | void * /*user_data*/) | 
|---|
| 333 | { | 
|---|
| 334 | // ### optimize | 
|---|
| 335 | QString s; | 
|---|
| 336 | s.reserve(4); | 
|---|
| 337 | s += QChar::fromUcs4(a); | 
|---|
| 338 | s += QChar::fromUcs4(b); | 
|---|
| 339 | QString normalized = s.normalized(QString::NormalizationForm_C); | 
|---|
| 340 |  | 
|---|
| 341 | QStringIterator it(normalized); | 
|---|
| 342 | Q_ASSERT(it.hasNext()); // size>0 | 
|---|
| 343 | *ab = it.next(); | 
|---|
| 344 |  | 
|---|
| 345 | return !it.hasNext(); // size==1 | 
|---|
| 346 | } | 
|---|
| 347 |  | 
|---|
| 348 | static hb_bool_t | 
|---|
| 349 | _hb_qt_unicode_decompose(hb_unicode_funcs_t * /*ufuncs*/, | 
|---|
| 350 | hb_codepoint_t ab, | 
|---|
| 351 | hb_codepoint_t *a, hb_codepoint_t *b, | 
|---|
| 352 | void * /*user_data*/) | 
|---|
| 353 | { | 
|---|
| 354 | // ### optimize | 
|---|
| 355 | if (QChar::decompositionTag(ab) != QChar::Canonical) // !NFD | 
|---|
| 356 | return false; | 
|---|
| 357 |  | 
|---|
| 358 | QString normalized = QChar::decomposition(ab); | 
|---|
| 359 | if (normalized.isEmpty()) | 
|---|
| 360 | return false; | 
|---|
| 361 |  | 
|---|
| 362 | QStringIterator it(normalized); | 
|---|
| 363 | Q_ASSERT(it.hasNext()); // size>0 | 
|---|
| 364 | *a = it.next(); | 
|---|
| 365 |  | 
|---|
| 366 | if (!it.hasNext()) { // size==1 | 
|---|
| 367 | *b = 0; | 
|---|
| 368 | return *a != ab; | 
|---|
| 369 | } | 
|---|
| 370 |  | 
|---|
| 371 | // size>1 | 
|---|
| 372 | *b = it.next(); | 
|---|
| 373 | if (!it.hasNext()) { // size==2 | 
|---|
| 374 | // Here's the ugly part: if ab decomposes to a single character and | 
|---|
| 375 | // that character decomposes again, we have to detect that and undo | 
|---|
| 376 | // the second part :-( | 
|---|
| 377 | const QString recomposed = normalized.normalized(QString::NormalizationForm_C); | 
|---|
| 378 | QStringIterator jt(recomposed); | 
|---|
| 379 | Q_ASSERT(jt.hasNext()); // size>0 | 
|---|
| 380 | const hb_codepoint_t c = jt.next(); | 
|---|
| 381 | if (c != *a && c != ab) { | 
|---|
| 382 | *a = c; | 
|---|
| 383 | *b = 0; | 
|---|
| 384 | } | 
|---|
| 385 | return true; | 
|---|
| 386 | } | 
|---|
| 387 |  | 
|---|
| 388 | // size>2 | 
|---|
| 389 | // If decomposed to more than two characters, take the last one, | 
|---|
| 390 | // and recompose the rest to get the first component | 
|---|
| 391 | do { | 
|---|
| 392 | *b = it.next(); | 
|---|
| 393 | } while (it.hasNext()); | 
|---|
| 394 | normalized.chop(QChar::requiresSurrogates(*b) ? 2 : 1); | 
|---|
| 395 | const QString recomposed = normalized.normalized(QString::NormalizationForm_C); | 
|---|
| 396 | QStringIterator jt(recomposed); | 
|---|
| 397 | Q_ASSERT(jt.hasNext()); // size>0 | 
|---|
| 398 | // We expect that recomposed has exactly one character now | 
|---|
| 399 | *a = jt.next(); | 
|---|
| 400 | return true; | 
|---|
| 401 | } | 
|---|
| 402 |  | 
|---|
| 403 |  | 
|---|
| 404 | struct _hb_unicode_funcs_t { | 
|---|
| 405 | _hb_unicode_funcs_t() | 
|---|
| 406 | { | 
|---|
| 407 | funcs = hb_unicode_funcs_create(NULL); | 
|---|
| 408 | hb_unicode_funcs_set_combining_class_func(funcs, _hb_qt_unicode_combining_class, NULL, NULL); | 
|---|
| 409 | hb_unicode_funcs_set_general_category_func(funcs, _hb_qt_unicode_general_category, NULL, NULL); | 
|---|
| 410 | hb_unicode_funcs_set_mirroring_func(funcs, _hb_qt_unicode_mirroring, NULL, NULL); | 
|---|
| 411 | hb_unicode_funcs_set_script_func(funcs, _hb_qt_unicode_script, NULL, NULL); | 
|---|
| 412 | hb_unicode_funcs_set_compose_func(funcs, _hb_qt_unicode_compose, NULL, NULL); | 
|---|
| 413 | hb_unicode_funcs_set_decompose_func(funcs, _hb_qt_unicode_decompose, NULL, NULL); | 
|---|
| 414 | } | 
|---|
| 415 | ~_hb_unicode_funcs_t() | 
|---|
| 416 | { | 
|---|
| 417 | hb_unicode_funcs_destroy(funcs); | 
|---|
| 418 | } | 
|---|
| 419 |  | 
|---|
| 420 | hb_unicode_funcs_t *funcs; | 
|---|
| 421 | }; | 
|---|
| 422 |  | 
|---|
| 423 | Q_GLOBAL_STATIC(_hb_unicode_funcs_t, qt_ufuncs) | 
|---|
| 424 |  | 
|---|
| 425 | hb_unicode_funcs_t *hb_qt_get_unicode_funcs() | 
|---|
| 426 | { | 
|---|
| 427 | return qt_ufuncs()->funcs; | 
|---|
| 428 | } | 
|---|
| 429 |  | 
|---|
| 430 |  | 
|---|
| 431 | // Font routines | 
|---|
| 432 |  | 
|---|
| 433 | static hb_bool_t | 
|---|
| 434 | _hb_qt_get_font_h_extents(hb_font_t * /*font*/, void *font_data, | 
|---|
| 435 | hb_font_extents_t *metrics, | 
|---|
| 436 | void * /*user_data*/) | 
|---|
| 437 | { | 
|---|
| 438 | QFontEngine *fe = static_cast<QFontEngine *>(font_data); | 
|---|
| 439 | Q_ASSERT(fe); | 
|---|
| 440 |  | 
|---|
| 441 | metrics->ascender = fe->ascent().value(); | 
|---|
| 442 | metrics->descender = fe->descent().value(); | 
|---|
| 443 | metrics->line_gap = fe->leading().value(); | 
|---|
| 444 |  | 
|---|
| 445 | return true; | 
|---|
| 446 | } | 
|---|
| 447 |  | 
|---|
| 448 | static hb_bool_t | 
|---|
| 449 | _hb_qt_font_get_nominal_glyph(hb_font_t * /*font*/, void *font_data, | 
|---|
| 450 | hb_codepoint_t unicode, | 
|---|
| 451 | hb_codepoint_t *glyph, | 
|---|
| 452 | void * /*user_data*/) | 
|---|
| 453 | { | 
|---|
| 454 | QFontEngine *fe = static_cast<QFontEngine *>(font_data); | 
|---|
| 455 | Q_ASSERT(fe); | 
|---|
| 456 |  | 
|---|
| 457 | *glyph = fe->glyphIndex(unicode); | 
|---|
| 458 |  | 
|---|
| 459 | return *glyph != 0; | 
|---|
| 460 | } | 
|---|
| 461 |  | 
|---|
| 462 | static hb_bool_t | 
|---|
| 463 | _hb_qt_font_get_variation_glyph(hb_font_t * /*font*/, void *font_data, | 
|---|
| 464 | hb_codepoint_t unicode, hb_codepoint_t /*variation_selector*/, | 
|---|
| 465 | hb_codepoint_t *glyph, | 
|---|
| 466 | void * /*user_data*/) | 
|---|
| 467 | { | 
|---|
| 468 | QFontEngine *fe = static_cast<QFontEngine *>(font_data); | 
|---|
| 469 | Q_ASSERT(fe); | 
|---|
| 470 |  | 
|---|
| 471 | // ### TODO add support for variation selectors | 
|---|
| 472 | *glyph = fe->glyphIndex(unicode); | 
|---|
| 473 |  | 
|---|
| 474 | return *glyph != 0; | 
|---|
| 475 | } | 
|---|
| 476 |  | 
|---|
| 477 | static hb_position_t | 
|---|
| 478 | _hb_qt_font_get_glyph_h_advance(hb_font_t *font, void *font_data, | 
|---|
| 479 | hb_codepoint_t glyph, | 
|---|
| 480 | void * /*user_data*/) | 
|---|
| 481 | { | 
|---|
| 482 | QFontEngine *fe = static_cast<QFontEngine *>(font_data); | 
|---|
| 483 | Q_ASSERT(fe); | 
|---|
| 484 |  | 
|---|
| 485 | QFixed advance; | 
|---|
| 486 |  | 
|---|
| 487 | QGlyphLayout g; | 
|---|
| 488 | g.numGlyphs = 1; | 
|---|
| 489 | g.glyphs = &glyph; | 
|---|
| 490 | g.advances = &advance; | 
|---|
| 491 |  | 
|---|
| 492 | fe->recalcAdvances(&g, QFontEngine::ShaperFlags(hb_qt_font_get_use_design_metrics(font))); | 
|---|
| 493 |  | 
|---|
| 494 | return advance.value(); | 
|---|
| 495 | } | 
|---|
| 496 |  | 
|---|
| 497 | static hb_position_t | 
|---|
| 498 | _hb_qt_font_get_glyph_h_kerning(hb_font_t *font, void *font_data, | 
|---|
| 499 | hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, | 
|---|
| 500 | void * /*user_data*/) | 
|---|
| 501 | { | 
|---|
| 502 | QFontEngine *fe = static_cast<QFontEngine *>(font_data); | 
|---|
| 503 | Q_ASSERT(fe); | 
|---|
| 504 |  | 
|---|
| 505 | glyph_t glyphs[2] = { first_glyph, second_glyph }; | 
|---|
| 506 | QFixed advance; | 
|---|
| 507 |  | 
|---|
| 508 | QGlyphLayout g; | 
|---|
| 509 | g.numGlyphs = 2; | 
|---|
| 510 | g.glyphs = glyphs; | 
|---|
| 511 | g.advances = &advance; | 
|---|
| 512 |  | 
|---|
| 513 | fe->doKerning(&g, QFontEngine::ShaperFlags(hb_qt_font_get_use_design_metrics(font))); | 
|---|
| 514 |  | 
|---|
| 515 | return advance.value(); | 
|---|
| 516 | } | 
|---|
| 517 |  | 
|---|
| 518 | static hb_bool_t | 
|---|
| 519 | _hb_qt_font_get_glyph_extents(hb_font_t * /*font*/, void *font_data, | 
|---|
| 520 | hb_codepoint_t glyph, | 
|---|
| 521 | hb_glyph_extents_t *extents, | 
|---|
| 522 | void * /*user_data*/) | 
|---|
| 523 | { | 
|---|
| 524 | QFontEngine *fe = static_cast<QFontEngine *>(font_data); | 
|---|
| 525 | Q_ASSERT(fe); | 
|---|
| 526 |  | 
|---|
| 527 | glyph_metrics_t gm = fe->boundingBox(glyph); | 
|---|
| 528 |  | 
|---|
| 529 | extents->x_bearing = gm.x.value(); | 
|---|
| 530 | extents->y_bearing = gm.y.value(); | 
|---|
| 531 | extents->width = gm.width.value(); | 
|---|
| 532 | extents->height = gm.height.value(); | 
|---|
| 533 |  | 
|---|
| 534 | return true; | 
|---|
| 535 | } | 
|---|
| 536 |  | 
|---|
| 537 | static hb_bool_t | 
|---|
| 538 | _hb_qt_font_get_glyph_contour_point(hb_font_t * /*font*/, void *font_data, | 
|---|
| 539 | hb_codepoint_t glyph, | 
|---|
| 540 | unsigned int point_index, hb_position_t *x, hb_position_t *y, | 
|---|
| 541 | void * /*user_data*/) | 
|---|
| 542 | { | 
|---|
| 543 | QFontEngine *fe = static_cast<QFontEngine *>(font_data); | 
|---|
| 544 | Q_ASSERT(fe); | 
|---|
| 545 |  | 
|---|
| 546 | QFixed xpos, ypos; | 
|---|
| 547 | quint32 numPoints = 1; | 
|---|
| 548 | if (Q_LIKELY(fe->getPointInOutline(glyph, 0, point_index, &xpos, &ypos, &numPoints) == 0)) { | 
|---|
| 549 | *x = xpos.value(); | 
|---|
| 550 | *y = ypos.value(); | 
|---|
| 551 | return true; | 
|---|
| 552 | } | 
|---|
| 553 |  | 
|---|
| 554 | *x = *y = 0; | 
|---|
| 555 | return false; | 
|---|
| 556 | } | 
|---|
| 557 |  | 
|---|
| 558 |  | 
|---|
| 559 | static hb_user_data_key_t _useDesignMetricsKey; | 
|---|
| 560 |  | 
|---|
| 561 | void hb_qt_font_set_use_design_metrics(hb_font_t *font, uint value) | 
|---|
| 562 | { | 
|---|
| 563 | hb_font_set_user_data(font, &_useDesignMetricsKey, (void *)quintptr(value), NULL, true); | 
|---|
| 564 | } | 
|---|
| 565 |  | 
|---|
| 566 | uint hb_qt_font_get_use_design_metrics(hb_font_t *font) | 
|---|
| 567 | { | 
|---|
| 568 | return quintptr(hb_font_get_user_data(font, &_useDesignMetricsKey)); | 
|---|
| 569 | } | 
|---|
| 570 |  | 
|---|
| 571 |  | 
|---|
| 572 | struct _hb_qt_font_funcs_t { | 
|---|
| 573 | _hb_qt_font_funcs_t() | 
|---|
| 574 | { | 
|---|
| 575 | funcs = hb_font_funcs_create(); | 
|---|
| 576 |  | 
|---|
| 577 | hb_font_funcs_set_font_h_extents_func(funcs, _hb_qt_get_font_h_extents, NULL, NULL); | 
|---|
| 578 | hb_font_funcs_set_nominal_glyph_func(funcs, _hb_qt_font_get_nominal_glyph, NULL, NULL); | 
|---|
| 579 | hb_font_funcs_set_variation_glyph_func(funcs, _hb_qt_font_get_variation_glyph, NULL, NULL); | 
|---|
| 580 | hb_font_funcs_set_glyph_h_advance_func(funcs, _hb_qt_font_get_glyph_h_advance, NULL, NULL); | 
|---|
| 581 | hb_font_funcs_set_glyph_h_kerning_func(funcs, _hb_qt_font_get_glyph_h_kerning, NULL, NULL); | 
|---|
| 582 | hb_font_funcs_set_glyph_extents_func(funcs, _hb_qt_font_get_glyph_extents, NULL, NULL); | 
|---|
| 583 | hb_font_funcs_set_glyph_contour_point_func(funcs, _hb_qt_font_get_glyph_contour_point, NULL, NULL); | 
|---|
| 584 |  | 
|---|
| 585 | hb_font_funcs_make_immutable(funcs); | 
|---|
| 586 | } | 
|---|
| 587 | ~_hb_qt_font_funcs_t() | 
|---|
| 588 | { | 
|---|
| 589 | hb_font_funcs_destroy(funcs); | 
|---|
| 590 | } | 
|---|
| 591 |  | 
|---|
| 592 | hb_font_funcs_t *funcs; | 
|---|
| 593 | }; | 
|---|
| 594 |  | 
|---|
| 595 | Q_GLOBAL_STATIC(_hb_qt_font_funcs_t, qt_ffuncs) | 
|---|
| 596 |  | 
|---|
| 597 |  | 
|---|
| 598 | static hb_blob_t * | 
|---|
| 599 | _hb_qt_reference_table(hb_face_t * /*face*/, hb_tag_t tag, void *user_data) | 
|---|
| 600 | { | 
|---|
| 601 | QFontEngine::FaceData *data = static_cast<QFontEngine::FaceData *>(user_data); | 
|---|
| 602 | Q_ASSERT(data); | 
|---|
| 603 |  | 
|---|
| 604 | qt_get_font_table_func_t get_font_table = data->get_font_table; | 
|---|
| 605 | Q_ASSERT(get_font_table); | 
|---|
| 606 |  | 
|---|
| 607 | uint length = 0; | 
|---|
| 608 | if (Q_UNLIKELY(!get_font_table(data->user_data, tag, 0, &length))) | 
|---|
| 609 | return hb_blob_get_empty(); | 
|---|
| 610 |  | 
|---|
| 611 | char *buffer = static_cast<char *>(malloc(length)); | 
|---|
| 612 | if (q_check_ptr(buffer) == nullptr) | 
|---|
| 613 | return nullptr; | 
|---|
| 614 |  | 
|---|
| 615 | if (Q_UNLIKELY(!get_font_table(data->user_data, tag, reinterpret_cast<uchar *>(buffer), &length))) | 
|---|
| 616 | return nullptr; | 
|---|
| 617 |  | 
|---|
| 618 | return hb_blob_create(const_cast<const char *>(buffer), length, | 
|---|
| 619 | HB_MEMORY_MODE_WRITABLE, | 
|---|
| 620 | buffer, free); | 
|---|
| 621 | } | 
|---|
| 622 |  | 
|---|
| 623 | static inline hb_face_t * | 
|---|
| 624 | _hb_qt_face_create(QFontEngine *fe) | 
|---|
| 625 | { | 
|---|
| 626 | QFontEngine::FaceData *data = static_cast<QFontEngine::FaceData *>(malloc(sizeof(QFontEngine::FaceData))); | 
|---|
| 627 | Q_CHECK_PTR(data); | 
|---|
| 628 | data->user_data = fe->faceData.user_data; | 
|---|
| 629 | data->get_font_table = fe->faceData.get_font_table; | 
|---|
| 630 |  | 
|---|
| 631 | hb_face_t *face = hb_face_create_for_tables(_hb_qt_reference_table, (void *)data, free); | 
|---|
| 632 |  | 
|---|
| 633 | hb_face_set_index(face, fe->faceId().index); | 
|---|
| 634 | hb_face_set_upem(face, fe->emSquareSize().truncate()); | 
|---|
| 635 |  | 
|---|
| 636 | return face; | 
|---|
| 637 | } | 
|---|
| 638 |  | 
|---|
| 639 | static void | 
|---|
| 640 | _hb_qt_face_release(void *user_data) | 
|---|
| 641 | { | 
|---|
| 642 | hb_face_destroy(static_cast<hb_face_t *>(user_data)); | 
|---|
| 643 | } | 
|---|
| 644 |  | 
|---|
| 645 | hb_face_t *hb_qt_face_get_for_engine(QFontEngine *fe) | 
|---|
| 646 | { | 
|---|
| 647 | Q_ASSERT(fe && fe->type() != QFontEngine::Multi); | 
|---|
| 648 |  | 
|---|
| 649 | if (Q_UNLIKELY(!fe->face_)) | 
|---|
| 650 | fe->face_ = QFontEngine::Holder(_hb_qt_face_create(fe), _hb_qt_face_release); | 
|---|
| 651 |  | 
|---|
| 652 | return static_cast<hb_face_t *>(fe->face_.get()); | 
|---|
| 653 | } | 
|---|
| 654 |  | 
|---|
| 655 |  | 
|---|
| 656 | static inline hb_font_t * | 
|---|
| 657 | _hb_qt_font_create(QFontEngine *fe) | 
|---|
| 658 | { | 
|---|
| 659 | hb_face_t *face = hb_qt_face_get_for_engine(fe); | 
|---|
| 660 |  | 
|---|
| 661 | hb_font_t *font = hb_font_create(face); | 
|---|
| 662 |  | 
|---|
| 663 | const qreal y_ppem = fe->fontDef.pixelSize; | 
|---|
| 664 | const qreal x_ppem = (fe->fontDef.pixelSize * fe->fontDef.stretch) / 100.0; | 
|---|
| 665 |  | 
|---|
| 666 | hb_font_set_funcs(font, qt_ffuncs()->funcs, fe, nullptr); | 
|---|
| 667 | hb_font_set_scale(font, QFixed::fromReal(x_ppem).value(), -QFixed::fromReal(y_ppem).value()); | 
|---|
| 668 | hb_font_set_ppem(font, int(x_ppem), int(y_ppem)); | 
|---|
| 669 |  | 
|---|
| 670 | hb_font_set_ptem(font, fe->fontDef.pointSize); | 
|---|
| 671 |  | 
|---|
| 672 | return font; | 
|---|
| 673 | } | 
|---|
| 674 |  | 
|---|
| 675 | static void | 
|---|
| 676 | _hb_qt_font_release(void *user_data) | 
|---|
| 677 | { | 
|---|
| 678 | hb_font_destroy(static_cast<hb_font_t *>(user_data)); | 
|---|
| 679 | } | 
|---|
| 680 |  | 
|---|
| 681 | hb_font_t *hb_qt_font_get_for_engine(QFontEngine *fe) | 
|---|
| 682 | { | 
|---|
| 683 | Q_ASSERT(fe && fe->type() != QFontEngine::Multi); | 
|---|
| 684 |  | 
|---|
| 685 | if (Q_UNLIKELY(!fe->font_)) | 
|---|
| 686 | fe->font_ = QFontEngine::Holder(_hb_qt_font_create(fe), _hb_qt_font_release); | 
|---|
| 687 |  | 
|---|
| 688 | return static_cast<hb_font_t *>(fe->font_.get()); | 
|---|
| 689 | } | 
|---|
| 690 |  | 
|---|
| 691 | QT_END_NAMESPACE | 
|---|
| 692 |  | 
|---|