J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 3 | * |
| 4 | * This code is free software; you can redistribute it and/or modify it |
| 5 | * under the terms of the GNU General Public License version 2 only, as |
| 6 | * published by the Free Software Foundation. Sun designates this |
| 7 | * particular file as subject to the "Classpath" exception as provided |
| 8 | * by Sun in the LICENSE file that accompanied this code. |
| 9 | * |
| 10 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 13 | * version 2 for more details (a copy is included in the LICENSE file that |
| 14 | * accompanied this code). |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License version |
| 17 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 18 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 19 | * |
| 20 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 21 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 22 | * have any questions. |
| 23 | * |
| 24 | */ |
| 25 | |
| 26 | /* |
| 27 | * |
| 28 | * (C) Copyright IBM Corp. 1998-2005 - All Rights Reserved |
| 29 | * |
| 30 | */ |
| 31 | |
| 32 | #include "LETypes.h" |
| 33 | #include "OpenTypeTables.h" |
| 34 | #include "OpenTypeUtilities.h" |
| 35 | #include "IndicReordering.h" |
| 36 | #include "LEGlyphStorage.h" |
| 37 | #include "MPreFixups.h" |
| 38 | |
| 39 | #define initFeatureTag LE_INIT_FEATURE_TAG |
| 40 | #define nuktFeatureTag LE_NUKT_FEATURE_TAG |
| 41 | #define akhnFeatureTag LE_AKHN_FEATURE_TAG |
| 42 | #define rphfFeatureTag LE_RPHF_FEATURE_TAG |
| 43 | #define blwfFeatureTag LE_BLWF_FEATURE_TAG |
| 44 | #define halfFeatureTag LE_HALF_FEATURE_TAG |
| 45 | #define pstfFeatureTag LE_PSTF_FEATURE_TAG |
| 46 | #define vatuFeatureTag LE_VATU_FEATURE_TAG |
| 47 | #define presFeatureTag LE_PRES_FEATURE_TAG |
| 48 | #define blwsFeatureTag LE_BLWS_FEATURE_TAG |
| 49 | #define abvsFeatureTag LE_ABVS_FEATURE_TAG |
| 50 | #define pstsFeatureTag LE_PSTS_FEATURE_TAG |
| 51 | #define halnFeatureTag LE_HALN_FEATURE_TAG |
| 52 | |
| 53 | #define blwmFeatureTag LE_BLWM_FEATURE_TAG |
| 54 | #define abvmFeatureTag LE_ABVM_FEATURE_TAG |
| 55 | #define distFeatureTag LE_DIST_FEATURE_TAG |
| 56 | |
| 57 | #define rphfFeatureMask 0x80000000UL |
| 58 | #define blwfFeatureMask 0x40000000UL |
| 59 | #define halfFeatureMask 0x20000000UL |
| 60 | #define pstfFeatureMask 0x10000000UL |
| 61 | #define nuktFeatureMask 0x08000000UL |
| 62 | #define akhnFeatureMask 0x04000000UL |
| 63 | #define vatuFeatureMask 0x02000000UL |
| 64 | #define presFeatureMask 0x01000000UL |
| 65 | #define blwsFeatureMask 0x00800000UL |
| 66 | #define abvsFeatureMask 0x00400000UL |
| 67 | #define pstsFeatureMask 0x00200000UL |
| 68 | #define halnFeatureMask 0x00100000UL |
| 69 | #define blwmFeatureMask 0x00080000UL |
| 70 | #define abvmFeatureMask 0x00040000UL |
| 71 | #define distFeatureMask 0x00020000UL |
| 72 | #define initFeatureMask 0x00010000UL |
| 73 | |
| 74 | class ReorderingOutput { |
| 75 | private: |
| 76 | le_int32 fOutIndex; |
| 77 | LEUnicode *fOutChars; |
| 78 | |
| 79 | LEGlyphStorage &fGlyphStorage; |
| 80 | |
| 81 | LEUnicode fMpre; |
| 82 | le_int32 fMpreIndex; |
| 83 | |
| 84 | LEUnicode fMbelow; |
| 85 | le_int32 fMbelowIndex; |
| 86 | |
| 87 | LEUnicode fMabove; |
| 88 | le_int32 fMaboveIndex; |
| 89 | |
| 90 | LEUnicode fMpost; |
| 91 | le_int32 fMpostIndex; |
| 92 | |
| 93 | LEUnicode fLengthMark; |
| 94 | le_int32 fLengthMarkIndex; |
| 95 | |
| 96 | LEUnicode fVirama; |
| 97 | le_int32 fViramaIndex; |
| 98 | |
| 99 | FeatureMask fMatraFeatures; |
| 100 | |
| 101 | le_int32 fMPreOutIndex; |
| 102 | MPreFixups *fMPreFixups; |
| 103 | |
| 104 | LEUnicode fVMabove; |
| 105 | LEUnicode fVMpost; |
| 106 | le_int32 fVMIndex; |
| 107 | FeatureMask fVMFeatures; |
| 108 | |
| 109 | LEUnicode fSMabove; |
| 110 | LEUnicode fSMbelow; |
| 111 | le_int32 fSMIndex; |
| 112 | FeatureMask fSMFeatures; |
| 113 | |
| 114 | void saveMatra(LEUnicode matra, le_int32 matraIndex, IndicClassTable::CharClass matraClass) |
| 115 | { |
| 116 | // FIXME: check if already set, or if not a matra... |
| 117 | if (IndicClassTable::isLengthMark(matraClass)) { |
| 118 | fLengthMark = matra; |
| 119 | fLengthMarkIndex = matraIndex; |
| 120 | } else if (IndicClassTable::isVirama(matraClass)) { |
| 121 | fVirama = matra; |
| 122 | fViramaIndex = matraIndex; |
| 123 | } else { |
| 124 | switch (matraClass & CF_POS_MASK) { |
| 125 | case CF_POS_BEFORE: |
| 126 | fMpre = matra; |
| 127 | fMpreIndex = matraIndex; |
| 128 | break; |
| 129 | |
| 130 | case CF_POS_BELOW: |
| 131 | fMbelow = matra; |
| 132 | fMbelowIndex = matraIndex; |
| 133 | break; |
| 134 | |
| 135 | case CF_POS_ABOVE: |
| 136 | fMabove = matra; |
| 137 | fMaboveIndex = matraIndex; |
| 138 | break; |
| 139 | |
| 140 | case CF_POS_AFTER: |
| 141 | fMpost = matra; |
| 142 | fMpostIndex = matraIndex; |
| 143 | break; |
| 144 | |
| 145 | default: |
| 146 | // can't get here... |
| 147 | break; |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | public: |
| 153 | ReorderingOutput(LEUnicode *outChars, LEGlyphStorage &glyphStorage, MPreFixups *mpreFixups) |
| 154 | : fOutIndex(0), fOutChars(outChars), fGlyphStorage(glyphStorage), |
| 155 | fMpre(0), fMpreIndex(0), fMbelow(0), fMbelowIndex(0), fMabove(0), fMaboveIndex(0), |
| 156 | fMpost(0), fMpostIndex(0), fLengthMark(0), fLengthMarkIndex(0), fVirama(0), fViramaIndex(0), |
| 157 | fMatraFeatures(0), fMPreOutIndex(-1), fMPreFixups(mpreFixups), |
| 158 | fVMabove(0), fVMpost(0), fVMIndex(0), fVMFeatures(0), |
| 159 | fSMabove(0), fSMbelow(0), fSMIndex(0), fSMFeatures(0) |
| 160 | { |
| 161 | // nothing else to do... |
| 162 | } |
| 163 | |
| 164 | ~ReorderingOutput() |
| 165 | { |
| 166 | // nothing to do here... |
| 167 | } |
| 168 | |
| 169 | void reset() |
| 170 | { |
| 171 | fMpre = fMbelow = fMabove = fMpost = fLengthMark = fVirama = 0; |
| 172 | fMPreOutIndex = -1; |
| 173 | |
| 174 | fVMabove = fVMpost = 0; |
| 175 | fSMabove = fSMbelow = 0; |
| 176 | } |
| 177 | |
| 178 | void writeChar(LEUnicode ch, le_uint32 charIndex, FeatureMask charFeatures) |
| 179 | { |
| 180 | LEErrorCode success = LE_NO_ERROR; |
| 181 | |
| 182 | fOutChars[fOutIndex] = ch; |
| 183 | |
| 184 | fGlyphStorage.setCharIndex(fOutIndex, charIndex, success); |
| 185 | fGlyphStorage.setAuxData(fOutIndex, charFeatures, success); |
| 186 | |
| 187 | fOutIndex += 1; |
| 188 | } |
| 189 | |
| 190 | le_bool noteMatra(const IndicClassTable *classTable, LEUnicode matra, le_uint32 matraIndex, |
| 191 | FeatureMask matraFeatures, le_bool wordStart) |
| 192 | { |
| 193 | IndicClassTable::CharClass matraClass = classTable->getCharClass(matra); |
| 194 | |
| 195 | fMatraFeatures = matraFeatures; |
| 196 | |
| 197 | if (wordStart) { |
| 198 | fMatraFeatures |= initFeatureMask; |
| 199 | } |
| 200 | |
| 201 | if (IndicClassTable::isMatra(matraClass)) { |
| 202 | if (IndicClassTable::isSplitMatra(matraClass)) { |
| 203 | const SplitMatra *splitMatra = classTable->getSplitMatra(matraClass); |
| 204 | int i; |
| 205 | |
| 206 | for (i = 0; i < 3 && (*splitMatra)[i] != 0; i += 1) { |
| 207 | LEUnicode piece = (*splitMatra)[i]; |
| 208 | IndicClassTable::CharClass pieceClass = classTable->getCharClass(piece); |
| 209 | |
| 210 | saveMatra(piece, matraIndex, pieceClass); |
| 211 | } |
| 212 | } else { |
| 213 | saveMatra(matra, matraIndex, matraClass); |
| 214 | } |
| 215 | |
| 216 | return TRUE; |
| 217 | } |
| 218 | |
| 219 | return FALSE; |
| 220 | } |
| 221 | |
| 222 | void noteVowelModifier(const IndicClassTable *classTable, LEUnicode vowelModifier, |
| 223 | le_uint32 vowelModifierIndex, FeatureMask vowelModifierFeatures) |
| 224 | { |
| 225 | IndicClassTable::CharClass vmClass = classTable->getCharClass(vowelModifier); |
| 226 | |
| 227 | fVMIndex = vowelModifierIndex; |
| 228 | fVMFeatures = vowelModifierFeatures; |
| 229 | |
| 230 | if (IndicClassTable::isVowelModifier(vmClass)) { |
| 231 | switch (vmClass & CF_POS_MASK) { |
| 232 | case CF_POS_ABOVE: |
| 233 | fVMabove = vowelModifier; |
| 234 | break; |
| 235 | |
| 236 | case CF_POS_AFTER: |
| 237 | fVMpost = vowelModifier; |
| 238 | break; |
| 239 | |
| 240 | default: |
| 241 | // FIXME: this is an error... |
| 242 | break; |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | void noteStressMark(const IndicClassTable *classTable, LEUnicode stressMark, |
| 248 | le_uint32 stressMarkIndex, FeatureMask stressMarkFeatures) |
| 249 | { |
| 250 | IndicClassTable::CharClass smClass = classTable->getCharClass(stressMark); |
| 251 | |
| 252 | fSMIndex = stressMarkIndex; |
| 253 | fSMFeatures = stressMarkFeatures; |
| 254 | |
| 255 | if (IndicClassTable::isStressMark(smClass)) { |
| 256 | switch (smClass & CF_POS_MASK) { |
| 257 | case CF_POS_ABOVE: |
| 258 | fSMabove = stressMark; |
| 259 | break; |
| 260 | |
| 261 | case CF_POS_BELOW: |
| 262 | fSMbelow = stressMark; |
| 263 | break; |
| 264 | |
| 265 | default: |
| 266 | // FIXME: this is an error... |
| 267 | break; |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | void noteBaseConsonant() |
| 273 | { |
| 274 | if (fMPreFixups != NULL && fMPreOutIndex >= 0) { |
| 275 | fMPreFixups->add(fOutIndex, fMPreOutIndex); |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | // Handles virama in Sinhala split vowels. |
| 280 | void writeVirama() |
| 281 | { |
| 282 | if (fVirama != 0) { |
| 283 | writeChar(fVirama, fViramaIndex, fMatraFeatures); |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | void writeMpre() |
| 288 | { |
| 289 | if (fMpre != 0) { |
| 290 | fMPreOutIndex = fOutIndex; |
| 291 | writeChar(fMpre, fMpreIndex, fMatraFeatures); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | void writeMbelow() |
| 296 | { |
| 297 | if (fMbelow != 0) { |
| 298 | writeChar(fMbelow, fMbelowIndex, fMatraFeatures); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | void writeMabove() |
| 303 | { |
| 304 | if (fMabove != 0) { |
| 305 | writeChar(fMabove, fMaboveIndex, fMatraFeatures); |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | void writeMpost() |
| 310 | { |
| 311 | if (fMpost != 0) { |
| 312 | writeChar(fMpost, fMpostIndex, fMatraFeatures); |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | void writeLengthMark() |
| 317 | { |
| 318 | if (fLengthMark != 0) { |
| 319 | writeChar(fLengthMark, fLengthMarkIndex, fMatraFeatures); |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | void writeVMabove() |
| 324 | { |
| 325 | if (fVMabove != 0) { |
| 326 | writeChar(fVMabove, fVMIndex, fVMFeatures); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | void writeVMpost() |
| 331 | { |
| 332 | if (fVMpost != 0) { |
| 333 | writeChar(fVMpost, fVMIndex, fVMFeatures); |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | void writeSMabove() |
| 338 | { |
| 339 | if (fSMabove != 0) { |
| 340 | writeChar(fSMabove, fSMIndex, fSMFeatures); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | void writeSMbelow() |
| 345 | { |
| 346 | if (fSMbelow != 0) { |
| 347 | writeChar(fSMbelow, fSMIndex, fSMFeatures); |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | le_int32 getOutputIndex() |
| 352 | { |
| 353 | return fOutIndex; |
| 354 | } |
| 355 | }; |
| 356 | |
| 357 | enum |
| 358 | { |
| 359 | C_DOTTED_CIRCLE = 0x25CC |
| 360 | }; |
| 361 | |
| 362 | // TODO: Find better names for these! |
| 363 | #define tagArray4 (nuktFeatureMask | akhnFeatureMask | vatuFeatureMask | presFeatureMask | \ |
| 364 | blwsFeatureMask | abvsFeatureMask | pstsFeatureMask | halnFeatureMask | \ |
| 365 | blwmFeatureMask | abvmFeatureMask | distFeatureMask) |
| 366 | #define tagArray3 (pstfFeatureMask | tagArray4) |
| 367 | #define tagArray2 (halfFeatureMask | tagArray3) |
| 368 | #define tagArray1 (blwfFeatureMask | tagArray2) |
| 369 | #define tagArray0 (rphfFeatureMask | tagArray1) |
| 370 | |
| 371 | static const FeatureMap featureMap[] = |
| 372 | { |
| 373 | {initFeatureTag, initFeatureMask}, |
| 374 | {nuktFeatureTag, nuktFeatureMask}, |
| 375 | {akhnFeatureTag, akhnFeatureMask}, |
| 376 | {rphfFeatureTag, rphfFeatureMask}, |
| 377 | {blwfFeatureTag, blwfFeatureMask}, |
| 378 | {halfFeatureTag, halfFeatureMask}, |
| 379 | {pstfFeatureTag, pstfFeatureMask}, |
| 380 | {vatuFeatureTag, vatuFeatureMask}, |
| 381 | {presFeatureTag, presFeatureMask}, |
| 382 | {blwsFeatureTag, blwsFeatureMask}, |
| 383 | {abvsFeatureTag, abvsFeatureMask}, |
| 384 | {pstsFeatureTag, pstsFeatureMask}, |
| 385 | {halnFeatureTag, halnFeatureMask}, |
| 386 | {blwmFeatureTag, blwmFeatureMask}, |
| 387 | {abvmFeatureTag, abvmFeatureMask}, |
| 388 | {distFeatureTag, distFeatureMask} |
| 389 | }; |
| 390 | |
| 391 | static const le_int32 featureCount = LE_ARRAY_SIZE(featureMap); |
| 392 | |
| 393 | static const le_int8 stateTable[][CC_COUNT] = |
| 394 | { |
| 395 | // xx vm sm iv i2 i3 ct cn nu dv s1 s2 s3 vr zw |
| 396 | { 1, 1, 1, 5, 8, 11, 3, 2, 1, 5, 9, 5, 1, 1, 1}, // 0 - ground state |
| 397 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 1 - exit state |
| 398 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, 5, 9, 5, 5, 4, -1}, // 2 - consonant with nukta |
| 399 | {-1, 6, 1, -1, -1, -1, -1, -1, 2, 5, 9, 5, 5, 4, -1}, // 3 - consonant |
| 400 | {-1, -1, -1, -1, -1, -1, 3, 2, -1, -1, -1, -1, -1, -1, 7}, // 4 - consonant virama |
| 401 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 5 - dependent vowels |
| 402 | {-1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 6 - vowel mark |
| 403 | {-1, -1, -1, -1, -1, -1, 3, 2, -1, -1, -1, -1, -1, -1, -1}, // 7 - ZWJ, ZWNJ |
| 404 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1}, // 8 - independent vowels that can take a virama |
| 405 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 5, -1, -1}, // 9 - first part of split vowel |
| 406 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, -1, -1}, // 10 - second part of split vowel |
| 407 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, 5, 9, 5, 5, 4, -1} // 11 - independent vowels that can take an iv |
| 408 | }; |
| 409 | |
| 410 | |
| 411 | const FeatureMap *IndicReordering::getFeatureMap(le_int32 &count) |
| 412 | { |
| 413 | count = featureCount; |
| 414 | |
| 415 | return featureMap; |
| 416 | } |
| 417 | |
| 418 | le_int32 IndicReordering::findSyllable(const IndicClassTable *classTable, |
| 419 | const LEUnicode *chars, le_int32 prev, le_int32 charCount) |
| 420 | { |
| 421 | le_int32 cursor = prev; |
| 422 | le_int8 state = 0; |
| 423 | |
| 424 | while (cursor < charCount) { |
| 425 | IndicClassTable::CharClass charClass = classTable->getCharClass(chars[cursor]); |
| 426 | |
| 427 | state = stateTable[state][charClass & CF_CLASS_MASK]; |
| 428 | |
| 429 | if (state < 0) { |
| 430 | break; |
| 431 | } |
| 432 | |
| 433 | cursor += 1; |
| 434 | } |
| 435 | |
| 436 | return cursor; |
| 437 | } |
| 438 | |
| 439 | le_int32 IndicReordering::reorder(const LEUnicode *chars, le_int32 charCount, le_int32 scriptCode, |
| 440 | LEUnicode *outChars, LEGlyphStorage &glyphStorage, |
| 441 | MPreFixups **outMPreFixups) |
| 442 | { |
| 443 | MPreFixups *mpreFixups = NULL; |
| 444 | const IndicClassTable *classTable = IndicClassTable::getScriptClassTable(scriptCode); |
| 445 | |
| 446 | if (classTable->scriptFlags & SF_MPRE_FIXUP) { |
| 447 | mpreFixups = new MPreFixups(charCount); |
| 448 | } |
| 449 | |
| 450 | ReorderingOutput output(outChars, glyphStorage, mpreFixups); |
| 451 | le_int32 i, prev = 0; |
| 452 | le_bool lastInWord = FALSE; |
| 453 | |
| 454 | while (prev < charCount) { |
| 455 | le_int32 syllable = findSyllable(classTable, chars, prev, charCount); |
| 456 | le_int32 matra, markStart = syllable; |
| 457 | |
| 458 | output.reset(); |
| 459 | |
| 460 | if (classTable->isStressMark(chars[markStart - 1])) { |
| 461 | markStart -= 1; |
| 462 | output.noteStressMark(classTable, chars[markStart], markStart, tagArray1); |
| 463 | } |
| 464 | |
| 465 | if (classTable->isVowelModifier(chars[markStart - 1])) { |
| 466 | markStart -= 1; |
| 467 | output.noteVowelModifier(classTable, chars[markStart], markStart, tagArray1); |
| 468 | } |
| 469 | |
| 470 | matra = markStart - 1; |
| 471 | |
| 472 | while (output.noteMatra(classTable, chars[matra], matra, tagArray1, !lastInWord) && matra != prev) { |
| 473 | matra -= 1; |
| 474 | } |
| 475 | |
| 476 | lastInWord = TRUE; |
| 477 | |
| 478 | switch (classTable->getCharClass(chars[prev]) & CF_CLASS_MASK) { |
| 479 | case CC_RESERVED: |
| 480 | lastInWord = FALSE; |
| 481 | /* fall through */ |
| 482 | |
| 483 | case CC_INDEPENDENT_VOWEL: |
| 484 | case CC_ZERO_WIDTH_MARK: |
| 485 | for (i = prev; i < syllable; i += 1) { |
| 486 | output.writeChar(chars[i], i, tagArray1); |
| 487 | } |
| 488 | |
| 489 | break; |
| 490 | |
| 491 | case CC_NUKTA: |
| 492 | case CC_VIRAMA: |
| 493 | output.writeChar(C_DOTTED_CIRCLE, prev, tagArray1); |
| 494 | output.writeChar(chars[prev], prev, tagArray1); |
| 495 | break; |
| 496 | |
| 497 | case CC_DEPENDENT_VOWEL: |
| 498 | case CC_SPLIT_VOWEL_PIECE_1: |
| 499 | case CC_SPLIT_VOWEL_PIECE_2: |
| 500 | case CC_SPLIT_VOWEL_PIECE_3: |
| 501 | case CC_VOWEL_MODIFIER: |
| 502 | case CC_STRESS_MARK: |
| 503 | output.writeMpre(); |
| 504 | |
| 505 | output.writeChar(C_DOTTED_CIRCLE, prev, tagArray1); |
| 506 | |
| 507 | output.writeMbelow(); |
| 508 | output.writeSMbelow(); |
| 509 | output.writeMabove(); |
| 510 | |
| 511 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) != 0) { |
| 512 | output.writeMpost(); |
| 513 | } |
| 514 | |
| 515 | if ((classTable->scriptFlags & SF_REPH_AFTER_BELOW) != 0) { |
| 516 | output.writeVMabove(); |
| 517 | output.writeSMabove(); // FIXME: there are no SM's in these scripts... |
| 518 | } |
| 519 | |
| 520 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) == 0) { |
| 521 | output.writeMpost(); |
| 522 | } |
| 523 | |
| 524 | output.writeLengthMark(); |
| 525 | output.writeVirama(); |
| 526 | |
| 527 | if ((classTable->scriptFlags & SF_REPH_AFTER_BELOW) == 0) { |
| 528 | output.writeVMabove(); |
| 529 | output.writeSMabove(); |
| 530 | } |
| 531 | |
| 532 | output.writeVMpost(); |
| 533 | break; |
| 534 | |
| 535 | case CC_INDEPENDENT_VOWEL_2: |
| 536 | case CC_INDEPENDENT_VOWEL_3: |
| 537 | case CC_CONSONANT: |
| 538 | case CC_CONSONANT_WITH_NUKTA: |
| 539 | { |
| 540 | le_uint32 length = markStart - prev; |
| 541 | le_int32 lastConsonant = markStart - 1; |
| 542 | le_int32 baseLimit = prev; |
| 543 | |
| 544 | // Check for REPH at front of syllable |
| 545 | if (length > 2 && classTable->isReph(chars[prev]) && classTable->isVirama(chars[prev + 1])) { |
| 546 | baseLimit += 2; |
| 547 | |
| 548 | // Check for eyelash RA, if the script supports it |
| 549 | if ((classTable->scriptFlags & SF_EYELASH_RA) != 0 && |
| 550 | chars[baseLimit] == C_SIGN_ZWJ) { |
| 551 | if (length > 3) { |
| 552 | baseLimit += 1; |
| 553 | } else { |
| 554 | baseLimit -= 2; |
| 555 | } |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | while (lastConsonant > baseLimit && !classTable->isConsonant(chars[lastConsonant])) { |
| 560 | lastConsonant -= 1; |
| 561 | } |
| 562 | |
| 563 | le_int32 baseConsonant = lastConsonant; |
| 564 | le_int32 postBase = lastConsonant + 1; |
| 565 | le_int32 postBaseLimit = classTable->scriptFlags & SF_POST_BASE_LIMIT_MASK; |
| 566 | le_bool seenVattu = FALSE; |
| 567 | le_bool seenBelowBaseForm = FALSE; |
| 568 | |
| 569 | if (postBase < markStart && classTable->isNukta(chars[postBase])) { |
| 570 | postBase += 1; |
| 571 | } |
| 572 | |
| 573 | while (baseConsonant > baseLimit) { |
| 574 | IndicClassTable::CharClass charClass = classTable->getCharClass(chars[baseConsonant]); |
| 575 | |
| 576 | if (IndicClassTable::isConsonant(charClass)) { |
| 577 | if (postBaseLimit == 0 || seenVattu || |
| 578 | (baseConsonant > baseLimit && !classTable->isVirama(chars[baseConsonant - 1])) || |
| 579 | !IndicClassTable::hasPostOrBelowBaseForm(charClass)) { |
| 580 | break; |
| 581 | } |
| 582 | |
| 583 | seenVattu = IndicClassTable::isVattu(charClass); |
| 584 | |
| 585 | if (IndicClassTable::hasPostBaseForm(charClass)) { |
| 586 | if (seenBelowBaseForm) { |
| 587 | break; |
| 588 | } |
| 589 | |
| 590 | postBase = baseConsonant; |
| 591 | } else if (IndicClassTable::hasBelowBaseForm(charClass)) { |
| 592 | seenBelowBaseForm = TRUE; |
| 593 | } |
| 594 | |
| 595 | postBaseLimit -= 1; |
| 596 | } |
| 597 | |
| 598 | baseConsonant -= 1; |
| 599 | } |
| 600 | |
| 601 | // Write Mpre |
| 602 | output.writeMpre(); |
| 603 | |
| 604 | // Write eyelash RA |
| 605 | // NOTE: baseLimit == prev + 3 iff eyelash RA present... |
| 606 | if (baseLimit == prev + 3) { |
| 607 | output.writeChar(chars[prev], prev, tagArray2); |
| 608 | output.writeChar(chars[prev + 1], prev + 1, tagArray2); |
| 609 | output.writeChar(chars[prev + 2], prev + 2, tagArray2); |
| 610 | } |
| 611 | |
| 612 | // write any pre-base consonants |
| 613 | le_bool supressVattu = TRUE; |
| 614 | |
| 615 | for (i = baseLimit; i < baseConsonant; i += 1) { |
| 616 | LEUnicode ch = chars[i]; |
| 617 | // Don't put 'blwf' on first consonant. |
| 618 | FeatureMask features = (i == baseLimit? tagArray2 : tagArray1); |
| 619 | IndicClassTable::CharClass charClass = classTable->getCharClass(ch); |
| 620 | |
| 621 | if (IndicClassTable::isConsonant(charClass)) { |
| 622 | if (IndicClassTable::isVattu(charClass) && supressVattu) { |
| 623 | features = tagArray4; |
| 624 | } |
| 625 | |
| 626 | supressVattu = IndicClassTable::isVattu(charClass); |
| 627 | } else if (IndicClassTable::isVirama(charClass) && chars[i + 1] == C_SIGN_ZWNJ) |
| 628 | { |
| 629 | features = tagArray4; |
| 630 | } |
| 631 | |
| 632 | output.writeChar(ch, i, features); |
| 633 | } |
| 634 | |
| 635 | le_int32 bcSpan = baseConsonant + 1; |
| 636 | |
| 637 | if (bcSpan < markStart && classTable->isNukta(chars[bcSpan])) { |
| 638 | bcSpan += 1; |
| 639 | } |
| 640 | |
| 641 | if (baseConsonant == lastConsonant && bcSpan < markStart && classTable->isVirama(chars[bcSpan])) { |
| 642 | bcSpan += 1; |
| 643 | |
| 644 | if (bcSpan < markStart && chars[bcSpan] == C_SIGN_ZWNJ) { |
| 645 | bcSpan += 1; |
| 646 | } |
| 647 | } |
| 648 | |
| 649 | // note the base consonant for post-GSUB fixups |
| 650 | output.noteBaseConsonant(); |
| 651 | |
| 652 | // write base consonant |
| 653 | for (i = baseConsonant; i < bcSpan; i += 1) { |
| 654 | output.writeChar(chars[i], i, tagArray4); |
| 655 | } |
| 656 | |
| 657 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) != 0) { |
| 658 | output.writeMbelow(); |
| 659 | output.writeSMbelow(); // FIXME: there are no SMs in these scripts... |
| 660 | output.writeMabove(); |
| 661 | output.writeMpost(); |
| 662 | } |
| 663 | |
| 664 | // write below-base consonants |
| 665 | if (baseConsonant != lastConsonant) { |
| 666 | for (i = bcSpan + 1; i < postBase; i += 1) { |
| 667 | output.writeChar(chars[i], i, tagArray1); |
| 668 | } |
| 669 | |
| 670 | if (postBase > lastConsonant) { |
| 671 | // write halant that was after base consonant |
| 672 | output.writeChar(chars[bcSpan], bcSpan, tagArray1); |
| 673 | } |
| 674 | } |
| 675 | |
| 676 | // write Mbelow, SMbelow, Mabove |
| 677 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) == 0) { |
| 678 | output.writeMbelow(); |
| 679 | output.writeSMbelow(); |
| 680 | output.writeMabove(); |
| 681 | } |
| 682 | |
| 683 | if ((classTable->scriptFlags & SF_REPH_AFTER_BELOW) != 0) { |
| 684 | if (baseLimit == prev + 2) { |
| 685 | output.writeChar(chars[prev], prev, tagArray0); |
| 686 | output.writeChar(chars[prev + 1], prev + 1, tagArray0); |
| 687 | } |
| 688 | |
| 689 | output.writeVMabove(); |
| 690 | output.writeSMabove(); // FIXME: there are no SM's in these scripts... |
| 691 | } |
| 692 | |
| 693 | // write post-base consonants |
| 694 | // FIXME: does this put the right tags on post-base consonants? |
| 695 | if (baseConsonant != lastConsonant) { |
| 696 | if (postBase <= lastConsonant) { |
| 697 | for (i = postBase; i <= lastConsonant; i += 1) { |
| 698 | output.writeChar(chars[i], i, tagArray3); |
| 699 | } |
| 700 | |
| 701 | // write halant that was after base consonant |
| 702 | output.writeChar(chars[bcSpan], bcSpan, tagArray1); |
| 703 | } |
| 704 | |
| 705 | // write the training halant, if there is one |
| 706 | if (lastConsonant < matra && classTable->isVirama(chars[matra])) { |
| 707 | output.writeChar(chars[matra], matra, tagArray4); |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | // write Mpost |
| 712 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) == 0) { |
| 713 | output.writeMpost(); |
| 714 | } |
| 715 | |
| 716 | output.writeLengthMark(); |
| 717 | output.writeVirama(); |
| 718 | |
| 719 | // write reph |
| 720 | if ((classTable->scriptFlags & SF_REPH_AFTER_BELOW) == 0) { |
| 721 | if (baseLimit == prev + 2) { |
| 722 | output.writeChar(chars[prev], prev, tagArray0); |
| 723 | output.writeChar(chars[prev + 1], prev + 1, tagArray0); |
| 724 | } |
| 725 | |
| 726 | output.writeVMabove(); |
| 727 | output.writeSMabove(); |
| 728 | } |
| 729 | |
| 730 | output.writeVMpost(); |
| 731 | |
| 732 | break; |
| 733 | } |
| 734 | |
| 735 | default: |
| 736 | break; |
| 737 | } |
| 738 | |
| 739 | prev = syllable; |
| 740 | } |
| 741 | |
| 742 | *outMPreFixups = mpreFixups; |
| 743 | |
| 744 | return output.getOutputIndex(); |
| 745 | } |
| 746 | |
| 747 | void IndicReordering::adjustMPres(MPreFixups *mpreFixups, LEGlyphStorage &glyphStorage) |
| 748 | { |
| 749 | if (mpreFixups != NULL) { |
| 750 | mpreFixups->apply(glyphStorage); |
| 751 | |
| 752 | delete mpreFixups; |
| 753 | } |
| 754 | } |