layout/generic/nsTextRunTransformations.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsTextRunTransformations.h"
michael@0 7
michael@0 8 #include "mozilla/MemoryReporting.h"
michael@0 9
michael@0 10 #include "nsGkAtoms.h"
michael@0 11 #include "nsStyleConsts.h"
michael@0 12 #include "nsStyleContext.h"
michael@0 13 #include "nsUnicodeProperties.h"
michael@0 14 #include "nsSpecialCasingData.h"
michael@0 15 #include "mozilla/gfx/2D.h"
michael@0 16 #include "nsTextFrameUtils.h"
michael@0 17 #include "nsIPersistentProperties2.h"
michael@0 18 #include "nsNetUtil.h"
michael@0 19
michael@0 20 // Unicode characters needing special casing treatment in tr/az languages
michael@0 21 #define LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE 0x0130
michael@0 22 #define LATIN_SMALL_LETTER_DOTLESS_I 0x0131
michael@0 23
michael@0 24 // Greek sigma needs custom handling for the lowercase transform; for details
michael@0 25 // see comments under "case NS_STYLE_TEXT_TRANSFORM_LOWERCASE" within
michael@0 26 // nsCaseTransformTextRunFactory::RebuildTextRun(), and bug 740120.
michael@0 27 #define GREEK_CAPITAL_LETTER_SIGMA 0x03A3
michael@0 28 #define GREEK_SMALL_LETTER_FINAL_SIGMA 0x03C2
michael@0 29 #define GREEK_SMALL_LETTER_SIGMA 0x03C3
michael@0 30
michael@0 31 // Custom uppercase mapping for Greek; see bug 307039 for details
michael@0 32 #define GREEK_LOWER_ALPHA 0x03B1
michael@0 33 #define GREEK_LOWER_ALPHA_TONOS 0x03AC
michael@0 34 #define GREEK_LOWER_ALPHA_OXIA 0x1F71
michael@0 35 #define GREEK_LOWER_EPSILON 0x03B5
michael@0 36 #define GREEK_LOWER_EPSILON_TONOS 0x03AD
michael@0 37 #define GREEK_LOWER_EPSILON_OXIA 0x1F73
michael@0 38 #define GREEK_LOWER_ETA 0x03B7
michael@0 39 #define GREEK_LOWER_ETA_TONOS 0x03AE
michael@0 40 #define GREEK_LOWER_ETA_OXIA 0x1F75
michael@0 41 #define GREEK_LOWER_IOTA 0x03B9
michael@0 42 #define GREEK_LOWER_IOTA_TONOS 0x03AF
michael@0 43 #define GREEK_LOWER_IOTA_OXIA 0x1F77
michael@0 44 #define GREEK_LOWER_IOTA_DIALYTIKA 0x03CA
michael@0 45 #define GREEK_LOWER_IOTA_DIALYTIKA_TONOS 0x0390
michael@0 46 #define GREEK_LOWER_IOTA_DIALYTIKA_OXIA 0x1FD3
michael@0 47 #define GREEK_LOWER_OMICRON 0x03BF
michael@0 48 #define GREEK_LOWER_OMICRON_TONOS 0x03CC
michael@0 49 #define GREEK_LOWER_OMICRON_OXIA 0x1F79
michael@0 50 #define GREEK_LOWER_UPSILON 0x03C5
michael@0 51 #define GREEK_LOWER_UPSILON_TONOS 0x03CD
michael@0 52 #define GREEK_LOWER_UPSILON_OXIA 0x1F7B
michael@0 53 #define GREEK_LOWER_UPSILON_DIALYTIKA 0x03CB
michael@0 54 #define GREEK_LOWER_UPSILON_DIALYTIKA_TONOS 0x03B0
michael@0 55 #define GREEK_LOWER_UPSILON_DIALYTIKA_OXIA 0x1FE3
michael@0 56 #define GREEK_LOWER_OMEGA 0x03C9
michael@0 57 #define GREEK_LOWER_OMEGA_TONOS 0x03CE
michael@0 58 #define GREEK_LOWER_OMEGA_OXIA 0x1F7D
michael@0 59 #define GREEK_UPPER_ALPHA 0x0391
michael@0 60 #define GREEK_UPPER_EPSILON 0x0395
michael@0 61 #define GREEK_UPPER_ETA 0x0397
michael@0 62 #define GREEK_UPPER_IOTA 0x0399
michael@0 63 #define GREEK_UPPER_IOTA_DIALYTIKA 0x03AA
michael@0 64 #define GREEK_UPPER_OMICRON 0x039F
michael@0 65 #define GREEK_UPPER_UPSILON 0x03A5
michael@0 66 #define GREEK_UPPER_UPSILON_DIALYTIKA 0x03AB
michael@0 67 #define GREEK_UPPER_OMEGA 0x03A9
michael@0 68 #define GREEK_UPPER_ALPHA_TONOS 0x0386
michael@0 69 #define GREEK_UPPER_ALPHA_OXIA 0x1FBB
michael@0 70 #define GREEK_UPPER_EPSILON_TONOS 0x0388
michael@0 71 #define GREEK_UPPER_EPSILON_OXIA 0x1FC9
michael@0 72 #define GREEK_UPPER_ETA_TONOS 0x0389
michael@0 73 #define GREEK_UPPER_ETA_OXIA 0x1FCB
michael@0 74 #define GREEK_UPPER_IOTA_TONOS 0x038A
michael@0 75 #define GREEK_UPPER_IOTA_OXIA 0x1FDB
michael@0 76 #define GREEK_UPPER_OMICRON_TONOS 0x038C
michael@0 77 #define GREEK_UPPER_OMICRON_OXIA 0x1FF9
michael@0 78 #define GREEK_UPPER_UPSILON_TONOS 0x038E
michael@0 79 #define GREEK_UPPER_UPSILON_OXIA 0x1FEB
michael@0 80 #define GREEK_UPPER_OMEGA_TONOS 0x038F
michael@0 81 #define GREEK_UPPER_OMEGA_OXIA 0x1FFB
michael@0 82 #define COMBINING_ACUTE_ACCENT 0x0301
michael@0 83 #define COMBINING_DIAERESIS 0x0308
michael@0 84 #define COMBINING_ACUTE_TONE_MARK 0x0341
michael@0 85 #define COMBINING_GREEK_DIALYTIKA_TONOS 0x0344
michael@0 86
michael@0 87 // When doing an Uppercase transform in Greek, we need to keep track of the
michael@0 88 // current state while iterating through the string, to recognize and process
michael@0 89 // diphthongs correctly. For clarity, we define a state for each vowel and
michael@0 90 // each vowel with accent, although a few of these do not actually need any
michael@0 91 // special treatment and could be folded into kStart.
michael@0 92 enum GreekCasingState {
michael@0 93 kStart,
michael@0 94 kAlpha,
michael@0 95 kEpsilon,
michael@0 96 kEta,
michael@0 97 kIota,
michael@0 98 kOmicron,
michael@0 99 kUpsilon,
michael@0 100 kOmega,
michael@0 101 kAlphaAcc,
michael@0 102 kEpsilonAcc,
michael@0 103 kEtaAcc,
michael@0 104 kIotaAcc,
michael@0 105 kOmicronAcc,
michael@0 106 kUpsilonAcc,
michael@0 107 kOmegaAcc,
michael@0 108 kOmicronUpsilon,
michael@0 109 kDiaeresis
michael@0 110 };
michael@0 111
michael@0 112 static uint32_t
michael@0 113 GreekUpperCase(uint32_t aCh, GreekCasingState* aState)
michael@0 114 {
michael@0 115 switch (aCh) {
michael@0 116 case GREEK_UPPER_ALPHA:
michael@0 117 case GREEK_LOWER_ALPHA:
michael@0 118 *aState = kAlpha;
michael@0 119 return GREEK_UPPER_ALPHA;
michael@0 120
michael@0 121 case GREEK_UPPER_EPSILON:
michael@0 122 case GREEK_LOWER_EPSILON:
michael@0 123 *aState = kEpsilon;
michael@0 124 return GREEK_UPPER_EPSILON;
michael@0 125
michael@0 126 case GREEK_UPPER_ETA:
michael@0 127 case GREEK_LOWER_ETA:
michael@0 128 *aState = kEta;
michael@0 129 return GREEK_UPPER_ETA;
michael@0 130
michael@0 131 case GREEK_UPPER_IOTA:
michael@0 132 *aState = kIota;
michael@0 133 return GREEK_UPPER_IOTA;
michael@0 134
michael@0 135 case GREEK_UPPER_OMICRON:
michael@0 136 case GREEK_LOWER_OMICRON:
michael@0 137 *aState = kOmicron;
michael@0 138 return GREEK_UPPER_OMICRON;
michael@0 139
michael@0 140 case GREEK_UPPER_UPSILON:
michael@0 141 switch (*aState) {
michael@0 142 case kOmicron:
michael@0 143 *aState = kOmicronUpsilon;
michael@0 144 break;
michael@0 145 default:
michael@0 146 *aState = kUpsilon;
michael@0 147 break;
michael@0 148 }
michael@0 149 return GREEK_UPPER_UPSILON;
michael@0 150
michael@0 151 case GREEK_UPPER_OMEGA:
michael@0 152 case GREEK_LOWER_OMEGA:
michael@0 153 *aState = kOmega;
michael@0 154 return GREEK_UPPER_OMEGA;
michael@0 155
michael@0 156 // iota and upsilon may be the second vowel of a diphthong
michael@0 157 case GREEK_LOWER_IOTA:
michael@0 158 switch (*aState) {
michael@0 159 case kAlphaAcc:
michael@0 160 case kEpsilonAcc:
michael@0 161 case kOmicronAcc:
michael@0 162 case kUpsilonAcc:
michael@0 163 *aState = kStart;
michael@0 164 return GREEK_UPPER_IOTA_DIALYTIKA;
michael@0 165 default:
michael@0 166 break;
michael@0 167 }
michael@0 168 *aState = kIota;
michael@0 169 return GREEK_UPPER_IOTA;
michael@0 170
michael@0 171 case GREEK_LOWER_UPSILON:
michael@0 172 switch (*aState) {
michael@0 173 case kAlphaAcc:
michael@0 174 case kEpsilonAcc:
michael@0 175 case kEtaAcc:
michael@0 176 case kOmicronAcc:
michael@0 177 *aState = kStart;
michael@0 178 return GREEK_UPPER_UPSILON_DIALYTIKA;
michael@0 179 case kOmicron:
michael@0 180 *aState = kOmicronUpsilon;
michael@0 181 break;
michael@0 182 default:
michael@0 183 *aState = kUpsilon;
michael@0 184 break;
michael@0 185 }
michael@0 186 return GREEK_UPPER_UPSILON;
michael@0 187
michael@0 188 case GREEK_UPPER_IOTA_DIALYTIKA:
michael@0 189 case GREEK_LOWER_IOTA_DIALYTIKA:
michael@0 190 case GREEK_UPPER_UPSILON_DIALYTIKA:
michael@0 191 case GREEK_LOWER_UPSILON_DIALYTIKA:
michael@0 192 case COMBINING_DIAERESIS:
michael@0 193 *aState = kDiaeresis;
michael@0 194 return ToUpperCase(aCh);
michael@0 195
michael@0 196 // remove accent if it follows a vowel or diaeresis,
michael@0 197 // and set appropriate state for diphthong detection
michael@0 198 case COMBINING_ACUTE_ACCENT:
michael@0 199 case COMBINING_ACUTE_TONE_MARK:
michael@0 200 switch (*aState) {
michael@0 201 case kAlpha:
michael@0 202 *aState = kAlphaAcc;
michael@0 203 return uint32_t(-1); // omit this char from result string
michael@0 204 case kEpsilon:
michael@0 205 *aState = kEpsilonAcc;
michael@0 206 return uint32_t(-1);
michael@0 207 case kEta:
michael@0 208 *aState = kEtaAcc;
michael@0 209 return uint32_t(-1);
michael@0 210 case kIota:
michael@0 211 *aState = kIotaAcc;
michael@0 212 return uint32_t(-1);
michael@0 213 case kOmicron:
michael@0 214 *aState = kOmicronAcc;
michael@0 215 return uint32_t(-1);
michael@0 216 case kUpsilon:
michael@0 217 *aState = kUpsilonAcc;
michael@0 218 return uint32_t(-1);
michael@0 219 case kOmicronUpsilon:
michael@0 220 *aState = kStart; // this completed a diphthong
michael@0 221 return uint32_t(-1);
michael@0 222 case kOmega:
michael@0 223 *aState = kOmegaAcc;
michael@0 224 return uint32_t(-1);
michael@0 225 case kDiaeresis:
michael@0 226 *aState = kStart;
michael@0 227 return uint32_t(-1);
michael@0 228 default:
michael@0 229 break;
michael@0 230 }
michael@0 231 break;
michael@0 232
michael@0 233 // combinations with dieresis+accent just strip the accent,
michael@0 234 // and reset to start state (don't form diphthong with following vowel)
michael@0 235 case GREEK_LOWER_IOTA_DIALYTIKA_TONOS:
michael@0 236 case GREEK_LOWER_IOTA_DIALYTIKA_OXIA:
michael@0 237 *aState = kStart;
michael@0 238 return GREEK_UPPER_IOTA_DIALYTIKA;
michael@0 239
michael@0 240 case GREEK_LOWER_UPSILON_DIALYTIKA_TONOS:
michael@0 241 case GREEK_LOWER_UPSILON_DIALYTIKA_OXIA:
michael@0 242 *aState = kStart;
michael@0 243 return GREEK_UPPER_UPSILON_DIALYTIKA;
michael@0 244
michael@0 245 case COMBINING_GREEK_DIALYTIKA_TONOS:
michael@0 246 *aState = kStart;
michael@0 247 return COMBINING_DIAERESIS;
michael@0 248
michael@0 249 // strip accents from vowels, and note the vowel seen so that we can detect
michael@0 250 // diphthongs where diaeresis needs to be added
michael@0 251 case GREEK_LOWER_ALPHA_TONOS:
michael@0 252 case GREEK_LOWER_ALPHA_OXIA:
michael@0 253 case GREEK_UPPER_ALPHA_TONOS:
michael@0 254 case GREEK_UPPER_ALPHA_OXIA:
michael@0 255 *aState = kAlphaAcc;
michael@0 256 return GREEK_UPPER_ALPHA;
michael@0 257
michael@0 258 case GREEK_LOWER_EPSILON_TONOS:
michael@0 259 case GREEK_LOWER_EPSILON_OXIA:
michael@0 260 case GREEK_UPPER_EPSILON_TONOS:
michael@0 261 case GREEK_UPPER_EPSILON_OXIA:
michael@0 262 *aState = kEpsilonAcc;
michael@0 263 return GREEK_UPPER_EPSILON;
michael@0 264
michael@0 265 case GREEK_LOWER_ETA_TONOS:
michael@0 266 case GREEK_LOWER_ETA_OXIA:
michael@0 267 case GREEK_UPPER_ETA_TONOS:
michael@0 268 case GREEK_UPPER_ETA_OXIA:
michael@0 269 *aState = kEtaAcc;
michael@0 270 return GREEK_UPPER_ETA;
michael@0 271
michael@0 272 case GREEK_LOWER_IOTA_TONOS:
michael@0 273 case GREEK_LOWER_IOTA_OXIA:
michael@0 274 case GREEK_UPPER_IOTA_TONOS:
michael@0 275 case GREEK_UPPER_IOTA_OXIA:
michael@0 276 *aState = kIotaAcc;
michael@0 277 return GREEK_UPPER_IOTA;
michael@0 278
michael@0 279 case GREEK_LOWER_OMICRON_TONOS:
michael@0 280 case GREEK_LOWER_OMICRON_OXIA:
michael@0 281 case GREEK_UPPER_OMICRON_TONOS:
michael@0 282 case GREEK_UPPER_OMICRON_OXIA:
michael@0 283 *aState = kOmicronAcc;
michael@0 284 return GREEK_UPPER_OMICRON;
michael@0 285
michael@0 286 case GREEK_LOWER_UPSILON_TONOS:
michael@0 287 case GREEK_LOWER_UPSILON_OXIA:
michael@0 288 case GREEK_UPPER_UPSILON_TONOS:
michael@0 289 case GREEK_UPPER_UPSILON_OXIA:
michael@0 290 switch (*aState) {
michael@0 291 case kOmicron:
michael@0 292 *aState = kStart; // this completed a diphthong
michael@0 293 break;
michael@0 294 default:
michael@0 295 *aState = kUpsilonAcc;
michael@0 296 break;
michael@0 297 }
michael@0 298 return GREEK_UPPER_UPSILON;
michael@0 299
michael@0 300 case GREEK_LOWER_OMEGA_TONOS:
michael@0 301 case GREEK_LOWER_OMEGA_OXIA:
michael@0 302 case GREEK_UPPER_OMEGA_TONOS:
michael@0 303 case GREEK_UPPER_OMEGA_OXIA:
michael@0 304 *aState = kOmegaAcc;
michael@0 305 return GREEK_UPPER_OMEGA;
michael@0 306 }
michael@0 307
michael@0 308 // all other characters just reset the state, and use standard mappings
michael@0 309 *aState = kStart;
michael@0 310 return ToUpperCase(aCh);
michael@0 311 }
michael@0 312
michael@0 313 nsTransformedTextRun *
michael@0 314 nsTransformedTextRun::Create(const gfxTextRunFactory::Parameters* aParams,
michael@0 315 nsTransformingTextRunFactory* aFactory,
michael@0 316 gfxFontGroup* aFontGroup,
michael@0 317 const char16_t* aString, uint32_t aLength,
michael@0 318 const uint32_t aFlags, nsStyleContext** aStyles,
michael@0 319 bool aOwnsFactory)
michael@0 320 {
michael@0 321 NS_ASSERTION(!(aFlags & gfxTextRunFactory::TEXT_IS_8BIT),
michael@0 322 "didn't expect text to be marked as 8-bit here");
michael@0 323
michael@0 324 void *storage = AllocateStorageForTextRun(sizeof(nsTransformedTextRun), aLength);
michael@0 325 if (!storage) {
michael@0 326 return nullptr;
michael@0 327 }
michael@0 328
michael@0 329 return new (storage) nsTransformedTextRun(aParams, aFactory, aFontGroup,
michael@0 330 aString, aLength,
michael@0 331 aFlags, aStyles, aOwnsFactory);
michael@0 332 }
michael@0 333
michael@0 334 void
michael@0 335 nsTransformedTextRun::SetCapitalization(uint32_t aStart, uint32_t aLength,
michael@0 336 bool* aCapitalization,
michael@0 337 gfxContext* aRefContext)
michael@0 338 {
michael@0 339 if (mCapitalize.IsEmpty()) {
michael@0 340 if (!mCapitalize.AppendElements(GetLength()))
michael@0 341 return;
michael@0 342 memset(mCapitalize.Elements(), 0, GetLength()*sizeof(bool));
michael@0 343 }
michael@0 344 memcpy(mCapitalize.Elements() + aStart, aCapitalization, aLength*sizeof(bool));
michael@0 345 mNeedsRebuild = true;
michael@0 346 }
michael@0 347
michael@0 348 bool
michael@0 349 nsTransformedTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
michael@0 350 uint8_t* aBreakBefore,
michael@0 351 gfxContext* aRefContext)
michael@0 352 {
michael@0 353 bool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength,
michael@0 354 aBreakBefore, aRefContext);
michael@0 355 if (changed) {
michael@0 356 mNeedsRebuild = true;
michael@0 357 }
michael@0 358 return changed;
michael@0 359 }
michael@0 360
michael@0 361 size_t
michael@0 362 nsTransformedTextRun::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
michael@0 363 {
michael@0 364 size_t total = gfxTextRun::SizeOfExcludingThis(aMallocSizeOf);
michael@0 365 total += mStyles.SizeOfExcludingThis(aMallocSizeOf);
michael@0 366 total += mCapitalize.SizeOfExcludingThis(aMallocSizeOf);
michael@0 367 if (mOwnsFactory) {
michael@0 368 total += aMallocSizeOf(mFactory);
michael@0 369 }
michael@0 370 return total;
michael@0 371 }
michael@0 372
michael@0 373 size_t
michael@0 374 nsTransformedTextRun::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
michael@0 375 {
michael@0 376 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 377 }
michael@0 378
michael@0 379 nsTransformedTextRun*
michael@0 380 nsTransformingTextRunFactory::MakeTextRun(const char16_t* aString, uint32_t aLength,
michael@0 381 const gfxTextRunFactory::Parameters* aParams,
michael@0 382 gfxFontGroup* aFontGroup, uint32_t aFlags,
michael@0 383 nsStyleContext** aStyles, bool aOwnsFactory)
michael@0 384 {
michael@0 385 return nsTransformedTextRun::Create(aParams, this, aFontGroup,
michael@0 386 aString, aLength, aFlags, aStyles, aOwnsFactory);
michael@0 387 }
michael@0 388
michael@0 389 nsTransformedTextRun*
michael@0 390 nsTransformingTextRunFactory::MakeTextRun(const uint8_t* aString, uint32_t aLength,
michael@0 391 const gfxTextRunFactory::Parameters* aParams,
michael@0 392 gfxFontGroup* aFontGroup, uint32_t aFlags,
michael@0 393 nsStyleContext** aStyles, bool aOwnsFactory)
michael@0 394 {
michael@0 395 // We'll only have a Unicode code path to minimize the amount of code needed
michael@0 396 // for these rarely used features
michael@0 397 NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString), aLength);
michael@0 398 return MakeTextRun(unicodeString.get(), aLength, aParams, aFontGroup,
michael@0 399 aFlags & ~(gfxFontGroup::TEXT_IS_PERSISTENT | gfxFontGroup::TEXT_IS_8BIT),
michael@0 400 aStyles, aOwnsFactory);
michael@0 401 }
michael@0 402
michael@0 403 void
michael@0 404 MergeCharactersInTextRun(gfxTextRun* aDest, gfxTextRun* aSrc,
michael@0 405 const bool* aCharsToMerge, const bool* aDeletedChars)
michael@0 406 {
michael@0 407 aDest->ResetGlyphRuns();
michael@0 408
michael@0 409 gfxTextRun::GlyphRunIterator iter(aSrc, 0, aSrc->GetLength());
michael@0 410 uint32_t offset = 0;
michael@0 411 nsAutoTArray<gfxTextRun::DetailedGlyph,2> glyphs;
michael@0 412 while (iter.NextRun()) {
michael@0 413 gfxTextRun::GlyphRun* run = iter.GetGlyphRun();
michael@0 414 nsresult rv = aDest->AddGlyphRun(run->mFont, run->mMatchType,
michael@0 415 offset, false);
michael@0 416 if (NS_FAILED(rv))
michael@0 417 return;
michael@0 418
michael@0 419 bool anyMissing = false;
michael@0 420 uint32_t mergeRunStart = iter.GetStringStart();
michael@0 421 const gfxTextRun::CompressedGlyph *srcGlyphs = aSrc->GetCharacterGlyphs();
michael@0 422 gfxTextRun::CompressedGlyph mergedGlyph = srcGlyphs[mergeRunStart];
michael@0 423 uint32_t stringEnd = iter.GetStringEnd();
michael@0 424 for (uint32_t k = iter.GetStringStart(); k < stringEnd; ++k) {
michael@0 425 const gfxTextRun::CompressedGlyph g = srcGlyphs[k];
michael@0 426 if (g.IsSimpleGlyph()) {
michael@0 427 if (!anyMissing) {
michael@0 428 gfxTextRun::DetailedGlyph details;
michael@0 429 details.mGlyphID = g.GetSimpleGlyph();
michael@0 430 details.mAdvance = g.GetSimpleAdvance();
michael@0 431 details.mXOffset = 0;
michael@0 432 details.mYOffset = 0;
michael@0 433 glyphs.AppendElement(details);
michael@0 434 }
michael@0 435 } else {
michael@0 436 if (g.IsMissing()) {
michael@0 437 anyMissing = true;
michael@0 438 glyphs.Clear();
michael@0 439 }
michael@0 440 if (g.GetGlyphCount() > 0) {
michael@0 441 glyphs.AppendElements(aSrc->GetDetailedGlyphs(k), g.GetGlyphCount());
michael@0 442 }
michael@0 443 }
michael@0 444
michael@0 445 if (k + 1 < iter.GetStringEnd() && aCharsToMerge[k + 1]) {
michael@0 446 // next char is supposed to merge with current, so loop without
michael@0 447 // writing current merged glyph to the destination
michael@0 448 continue;
michael@0 449 }
michael@0 450
michael@0 451 // If the start of the merge run is actually a character that should
michael@0 452 // have been merged with the previous character (this can happen
michael@0 453 // if there's a font change in the middle of a case-mapped character,
michael@0 454 // that decomposed into a sequence of base+diacritics, for example),
michael@0 455 // just discard the entire merge run. See comment at start of this
michael@0 456 // function.
michael@0 457 NS_WARN_IF_FALSE(!aCharsToMerge[mergeRunStart],
michael@0 458 "unable to merge across a glyph run boundary, "
michael@0 459 "glyph(s) discarded");
michael@0 460 if (!aCharsToMerge[mergeRunStart]) {
michael@0 461 if (anyMissing) {
michael@0 462 mergedGlyph.SetMissing(glyphs.Length());
michael@0 463 } else {
michael@0 464 mergedGlyph.SetComplex(mergedGlyph.IsClusterStart(),
michael@0 465 mergedGlyph.IsLigatureGroupStart(),
michael@0 466 glyphs.Length());
michael@0 467 }
michael@0 468 aDest->SetGlyphs(offset, mergedGlyph, glyphs.Elements());
michael@0 469 ++offset;
michael@0 470
michael@0 471 while (offset < aDest->GetLength() && aDeletedChars[offset]) {
michael@0 472 aDest->SetGlyphs(offset++, gfxTextRun::CompressedGlyph(), nullptr);
michael@0 473 }
michael@0 474 }
michael@0 475
michael@0 476 glyphs.Clear();
michael@0 477 anyMissing = false;
michael@0 478 mergeRunStart = k + 1;
michael@0 479 if (mergeRunStart < stringEnd) {
michael@0 480 mergedGlyph = srcGlyphs[mergeRunStart];
michael@0 481 }
michael@0 482 }
michael@0 483 NS_ASSERTION(glyphs.Length() == 0,
michael@0 484 "Leftover glyphs, don't request merging of the last character with its next!");
michael@0 485 }
michael@0 486 NS_ASSERTION(offset == aDest->GetLength(), "Bad offset calculations");
michael@0 487 }
michael@0 488
michael@0 489 gfxTextRunFactory::Parameters
michael@0 490 GetParametersForInner(nsTransformedTextRun* aTextRun, uint32_t* aFlags,
michael@0 491 gfxContext* aRefContext)
michael@0 492 {
michael@0 493 gfxTextRunFactory::Parameters params =
michael@0 494 { aRefContext, nullptr, nullptr,
michael@0 495 nullptr, 0, aTextRun->GetAppUnitsPerDevUnit()
michael@0 496 };
michael@0 497 *aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT;
michael@0 498 return params;
michael@0 499 }
michael@0 500
michael@0 501 void
michael@0 502 nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
michael@0 503 gfxContext* aRefContext)
michael@0 504 {
michael@0 505 gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
michael@0 506 gfxFontStyle fontStyle = *fontGroup->GetStyle();
michael@0 507 fontStyle.size *= 0.8;
michael@0 508 nsRefPtr<gfxFontGroup> smallFont = fontGroup->Copy(&fontStyle);
michael@0 509 if (!smallFont)
michael@0 510 return;
michael@0 511
michael@0 512 uint32_t flags;
michael@0 513 gfxTextRunFactory::Parameters innerParams =
michael@0 514 GetParametersForInner(aTextRun, &flags, aRefContext);
michael@0 515
michael@0 516 uint32_t length = aTextRun->GetLength();
michael@0 517 const char16_t* str = aTextRun->mString.BeginReading();
michael@0 518 nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
michael@0 519 // Create a textrun so we can check cluster-start properties
michael@0 520 nsAutoPtr<gfxTextRun> inner(fontGroup->MakeTextRun(str, length, &innerParams, flags));
michael@0 521 if (!inner.get())
michael@0 522 return;
michael@0 523
michael@0 524 nsCaseTransformTextRunFactory uppercaseFactory(nullptr, true);
michael@0 525
michael@0 526 aTextRun->ResetGlyphRuns();
michael@0 527
michael@0 528 uint32_t runStart = 0;
michael@0 529 nsAutoTArray<nsStyleContext*,50> styleArray;
michael@0 530 nsAutoTArray<uint8_t,50> canBreakBeforeArray;
michael@0 531
michael@0 532 enum RunCaseState {
michael@0 533 kUpperOrCaseless, // will be untouched by font-variant:small-caps
michael@0 534 kLowercase, // will be uppercased and reduced
michael@0 535 kSpecialUpper // specials: don't shrink, but apply uppercase mapping
michael@0 536 };
michael@0 537 RunCaseState runCase = kUpperOrCaseless;
michael@0 538
michael@0 539 // Note that this loop runs from 0 to length *inclusive*, so the last
michael@0 540 // iteration is in effect beyond the end of the input text, to give a
michael@0 541 // chance to finish the last casing run we've found.
michael@0 542 // The last iteration, when i==length, must not attempt to look at the
michael@0 543 // character position [i] or the style data for styles[i], as this would
michael@0 544 // be beyond the valid length of the textrun or its style array.
michael@0 545 for (uint32_t i = 0; i <= length; ++i) {
michael@0 546 RunCaseState chCase = kUpperOrCaseless;
michael@0 547 // Unless we're at the end, figure out what treatment the current
michael@0 548 // character will need.
michael@0 549 if (i < length) {
michael@0 550 nsStyleContext* styleContext = styles[i];
michael@0 551 // Characters that aren't the start of a cluster are ignored here. They
michael@0 552 // get added to whatever lowercase/non-lowercase run we're in.
michael@0 553 if (!inner->IsClusterStart(i)) {
michael@0 554 chCase = runCase;
michael@0 555 } else {
michael@0 556 if (styleContext->StyleFont()->mFont.variant == NS_STYLE_FONT_VARIANT_SMALL_CAPS) {
michael@0 557 uint32_t ch = str[i];
michael@0 558 if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 && NS_IS_LOW_SURROGATE(str[i + 1])) {
michael@0 559 ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
michael@0 560 }
michael@0 561 uint32_t ch2 = ToUpperCase(ch);
michael@0 562 if (ch != ch2 || mozilla::unicode::SpecialUpper(ch)) {
michael@0 563 chCase = kLowercase;
michael@0 564 } else if (styleContext->StyleFont()->mLanguage == nsGkAtoms::el) {
michael@0 565 // In Greek, check for characters that will be modified by the
michael@0 566 // GreekUpperCase mapping - this catches accented capitals where
michael@0 567 // the accent is to be removed (bug 307039). These are handled by
michael@0 568 // a transformed child run using the full-size font.
michael@0 569 GreekCasingState state = kStart; // don't need exact context here
michael@0 570 ch2 = GreekUpperCase(ch, &state);
michael@0 571 if (ch != ch2) {
michael@0 572 chCase = kSpecialUpper;
michael@0 573 }
michael@0 574 }
michael@0 575 } else {
michael@0 576 // Don't transform the character! I.e., pretend that it's not lowercase
michael@0 577 }
michael@0 578 }
michael@0 579 }
michael@0 580
michael@0 581 // At the end of the text, or when the current character needs different
michael@0 582 // casing treatment from the current run, finish the run-in-progress
michael@0 583 // and prepare to accumulate a new run.
michael@0 584 // Note that we do not look at any source data for offset [i] here,
michael@0 585 // as that would be invalid in the case where i==length.
michael@0 586 if ((i == length || runCase != chCase) && runStart < i) {
michael@0 587 nsAutoPtr<nsTransformedTextRun> transformedChild;
michael@0 588 nsAutoPtr<gfxTextRun> cachedChild;
michael@0 589 gfxTextRun* child;
michael@0 590
michael@0 591 switch (runCase) {
michael@0 592 case kUpperOrCaseless:
michael@0 593 cachedChild =
michael@0 594 fontGroup->MakeTextRun(str + runStart, i - runStart, &innerParams,
michael@0 595 flags);
michael@0 596 child = cachedChild.get();
michael@0 597 break;
michael@0 598 case kLowercase:
michael@0 599 transformedChild =
michael@0 600 uppercaseFactory.MakeTextRun(str + runStart, i - runStart,
michael@0 601 &innerParams, smallFont, flags,
michael@0 602 styleArray.Elements(), false);
michael@0 603 child = transformedChild;
michael@0 604 break;
michael@0 605 case kSpecialUpper:
michael@0 606 transformedChild =
michael@0 607 uppercaseFactory.MakeTextRun(str + runStart, i - runStart,
michael@0 608 &innerParams, fontGroup, flags,
michael@0 609 styleArray.Elements(), false);
michael@0 610 child = transformedChild;
michael@0 611 break;
michael@0 612 }
michael@0 613 if (!child)
michael@0 614 return;
michael@0 615 // Copy potential linebreaks into child so they're preserved
michael@0 616 // (and also child will be shaped appropriately)
michael@0 617 NS_ASSERTION(canBreakBeforeArray.Length() == i - runStart,
michael@0 618 "lost some break-before values?");
michael@0 619 child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
michael@0 620 canBreakBeforeArray.Elements(), aRefContext);
michael@0 621 if (transformedChild) {
michael@0 622 transformedChild->FinishSettingProperties(aRefContext);
michael@0 623 }
michael@0 624 aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), runStart);
michael@0 625
michael@0 626 runStart = i;
michael@0 627 styleArray.Clear();
michael@0 628 canBreakBeforeArray.Clear();
michael@0 629 }
michael@0 630
michael@0 631 if (i < length) {
michael@0 632 runCase = chCase;
michael@0 633 styleArray.AppendElement(styles[i]);
michael@0 634 canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
michael@0 635 }
michael@0 636 }
michael@0 637 }
michael@0 638
michael@0 639 void
michael@0 640 nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
michael@0 641 gfxContext* aRefContext)
michael@0 642 {
michael@0 643 uint32_t length = aTextRun->GetLength();
michael@0 644 const char16_t* str = aTextRun->mString.BeginReading();
michael@0 645 nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
michael@0 646
michael@0 647 nsAutoString convertedString;
michael@0 648 nsAutoTArray<bool,50> charsToMergeArray;
michael@0 649 nsAutoTArray<bool,50> deletedCharsArray;
michael@0 650 nsAutoTArray<nsStyleContext*,50> styleArray;
michael@0 651 nsAutoTArray<uint8_t,50> canBreakBeforeArray;
michael@0 652 bool mergeNeeded = false;
michael@0 653
michael@0 654 // Some languages have special casing conventions that differ from the
michael@0 655 // default Unicode mappings.
michael@0 656 // The enum values here are named for well-known exemplar languages that
michael@0 657 // exhibit the behavior in question; multiple lang tags may map to the
michael@0 658 // same setting here, if the behavior is shared by other languages.
michael@0 659 enum {
michael@0 660 eNone, // default non-lang-specific behavior
michael@0 661 eTurkish, // preserve dotted/dotless-i distinction in uppercase
michael@0 662 eDutch, // treat "ij" digraph as a unit for capitalization
michael@0 663 eGreek // strip accent when uppercasing Greek vowels
michael@0 664 } languageSpecificCasing = eNone;
michael@0 665
michael@0 666 const nsIAtom* lang = nullptr;
michael@0 667 bool capitalizeDutchIJ = false;
michael@0 668 bool prevIsLetter = false;
michael@0 669 uint32_t sigmaIndex = uint32_t(-1);
michael@0 670 nsIUGenCategory::nsUGenCategory cat;
michael@0 671 GreekCasingState greekState = kStart;
michael@0 672 uint32_t i;
michael@0 673 for (i = 0; i < length; ++i) {
michael@0 674 uint32_t ch = str[i];
michael@0 675 nsStyleContext* styleContext = styles[i];
michael@0 676
michael@0 677 uint8_t style = mAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE
michael@0 678 : styleContext->StyleText()->mTextTransform;
michael@0 679 int extraChars = 0;
michael@0 680 const mozilla::unicode::MultiCharMapping *mcm;
michael@0 681
michael@0 682 if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 && NS_IS_LOW_SURROGATE(str[i + 1])) {
michael@0 683 ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
michael@0 684 }
michael@0 685
michael@0 686 if (lang != styleContext->StyleFont()->mLanguage) {
michael@0 687 lang = styleContext->StyleFont()->mLanguage;
michael@0 688 if (lang == nsGkAtoms::tr || lang == nsGkAtoms::az ||
michael@0 689 lang == nsGkAtoms::ba || lang == nsGkAtoms::crh ||
michael@0 690 lang == nsGkAtoms::tt) {
michael@0 691 languageSpecificCasing = eTurkish;
michael@0 692 } else if (lang == nsGkAtoms::nl) {
michael@0 693 languageSpecificCasing = eDutch;
michael@0 694 } else if (lang == nsGkAtoms::el) {
michael@0 695 languageSpecificCasing = eGreek;
michael@0 696 greekState = kStart;
michael@0 697 } else {
michael@0 698 languageSpecificCasing = eNone;
michael@0 699 }
michael@0 700 }
michael@0 701
michael@0 702 switch (style) {
michael@0 703 case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
michael@0 704 if (languageSpecificCasing == eTurkish) {
michael@0 705 if (ch == 'I') {
michael@0 706 ch = LATIN_SMALL_LETTER_DOTLESS_I;
michael@0 707 prevIsLetter = true;
michael@0 708 sigmaIndex = uint32_t(-1);
michael@0 709 break;
michael@0 710 }
michael@0 711 if (ch == LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE) {
michael@0 712 ch = 'i';
michael@0 713 prevIsLetter = true;
michael@0 714 sigmaIndex = uint32_t(-1);
michael@0 715 break;
michael@0 716 }
michael@0 717 }
michael@0 718
michael@0 719 // Special lowercasing behavior for Greek Sigma: note that this is listed
michael@0 720 // as context-sensitive in Unicode's SpecialCasing.txt, but is *not* a
michael@0 721 // language-specific mapping; it applies regardless of the language of
michael@0 722 // the element.
michael@0 723 //
michael@0 724 // The lowercase mapping for CAPITAL SIGMA should be to SMALL SIGMA (i.e.
michael@0 725 // the non-final form) whenever there is a following letter, or when the
michael@0 726 // CAPITAL SIGMA occurs in isolation (neither preceded nor followed by a
michael@0 727 // LETTER); and to FINAL SIGMA when it is preceded by another letter but
michael@0 728 // not followed by one.
michael@0 729 //
michael@0 730 // To implement the context-sensitive nature of this mapping, we keep
michael@0 731 // track of whether the previous character was a letter. If not, CAPITAL
michael@0 732 // SIGMA will map directly to SMALL SIGMA. If the previous character
michael@0 733 // was a letter, CAPITAL SIGMA maps to FINAL SIGMA and we record the
michael@0 734 // position in the converted string; if we then encounter another letter,
michael@0 735 // that FINAL SIGMA is replaced with a standard SMALL SIGMA.
michael@0 736
michael@0 737 cat = mozilla::unicode::GetGenCategory(ch);
michael@0 738
michael@0 739 // If sigmaIndex is not -1, it marks where we have provisionally mapped
michael@0 740 // a CAPITAL SIGMA to FINAL SIGMA; if we now find another letter, we
michael@0 741 // need to change it to SMALL SIGMA.
michael@0 742 if (sigmaIndex != uint32_t(-1)) {
michael@0 743 if (cat == nsIUGenCategory::kLetter) {
michael@0 744 convertedString.SetCharAt(GREEK_SMALL_LETTER_SIGMA, sigmaIndex);
michael@0 745 }
michael@0 746 }
michael@0 747
michael@0 748 if (ch == GREEK_CAPITAL_LETTER_SIGMA) {
michael@0 749 // If preceding char was a letter, map to FINAL instead of SMALL,
michael@0 750 // and note where it occurred by setting sigmaIndex; we'll change it
michael@0 751 // to standard SMALL SIGMA later if another letter follows
michael@0 752 if (prevIsLetter) {
michael@0 753 ch = GREEK_SMALL_LETTER_FINAL_SIGMA;
michael@0 754 sigmaIndex = convertedString.Length();
michael@0 755 } else {
michael@0 756 // CAPITAL SIGMA not preceded by a letter is unconditionally mapped
michael@0 757 // to SMALL SIGMA
michael@0 758 ch = GREEK_SMALL_LETTER_SIGMA;
michael@0 759 sigmaIndex = uint32_t(-1);
michael@0 760 }
michael@0 761 prevIsLetter = true;
michael@0 762 break;
michael@0 763 }
michael@0 764
michael@0 765 // ignore diacritics for the purpose of contextual sigma mapping;
michael@0 766 // otherwise, reset prevIsLetter appropriately and clear the
michael@0 767 // sigmaIndex marker
michael@0 768 if (cat != nsIUGenCategory::kMark) {
michael@0 769 prevIsLetter = (cat == nsIUGenCategory::kLetter);
michael@0 770 sigmaIndex = uint32_t(-1);
michael@0 771 }
michael@0 772
michael@0 773 mcm = mozilla::unicode::SpecialLower(ch);
michael@0 774 if (mcm) {
michael@0 775 int j = 0;
michael@0 776 while (j < 2 && mcm->mMappedChars[j + 1]) {
michael@0 777 convertedString.Append(mcm->mMappedChars[j]);
michael@0 778 ++extraChars;
michael@0 779 ++j;
michael@0 780 }
michael@0 781 ch = mcm->mMappedChars[j];
michael@0 782 break;
michael@0 783 }
michael@0 784
michael@0 785 ch = ToLowerCase(ch);
michael@0 786 break;
michael@0 787
michael@0 788 case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
michael@0 789 if (languageSpecificCasing == eTurkish && ch == 'i') {
michael@0 790 ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
michael@0 791 break;
michael@0 792 }
michael@0 793
michael@0 794 if (languageSpecificCasing == eGreek) {
michael@0 795 ch = GreekUpperCase(ch, &greekState);
michael@0 796 break;
michael@0 797 }
michael@0 798
michael@0 799 mcm = mozilla::unicode::SpecialUpper(ch);
michael@0 800 if (mcm) {
michael@0 801 int j = 0;
michael@0 802 while (j < 2 && mcm->mMappedChars[j + 1]) {
michael@0 803 convertedString.Append(mcm->mMappedChars[j]);
michael@0 804 ++extraChars;
michael@0 805 ++j;
michael@0 806 }
michael@0 807 ch = mcm->mMappedChars[j];
michael@0 808 break;
michael@0 809 }
michael@0 810
michael@0 811 ch = ToUpperCase(ch);
michael@0 812 break;
michael@0 813
michael@0 814 case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
michael@0 815 if (capitalizeDutchIJ && ch == 'j') {
michael@0 816 ch = 'J';
michael@0 817 capitalizeDutchIJ = false;
michael@0 818 break;
michael@0 819 }
michael@0 820 capitalizeDutchIJ = false;
michael@0 821 if (i < aTextRun->mCapitalize.Length() && aTextRun->mCapitalize[i]) {
michael@0 822 if (languageSpecificCasing == eTurkish && ch == 'i') {
michael@0 823 ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
michael@0 824 break;
michael@0 825 }
michael@0 826 if (languageSpecificCasing == eDutch && ch == 'i') {
michael@0 827 ch = 'I';
michael@0 828 capitalizeDutchIJ = true;
michael@0 829 break;
michael@0 830 }
michael@0 831
michael@0 832 mcm = mozilla::unicode::SpecialTitle(ch);
michael@0 833 if (mcm) {
michael@0 834 int j = 0;
michael@0 835 while (j < 2 && mcm->mMappedChars[j + 1]) {
michael@0 836 convertedString.Append(mcm->mMappedChars[j]);
michael@0 837 ++extraChars;
michael@0 838 ++j;
michael@0 839 }
michael@0 840 ch = mcm->mMappedChars[j];
michael@0 841 break;
michael@0 842 }
michael@0 843
michael@0 844 ch = ToTitleCase(ch);
michael@0 845 }
michael@0 846 break;
michael@0 847
michael@0 848 case NS_STYLE_TEXT_TRANSFORM_FULLWIDTH:
michael@0 849 ch = mozilla::unicode::GetFullWidth(ch);
michael@0 850 break;
michael@0 851
michael@0 852 default:
michael@0 853 break;
michael@0 854 }
michael@0 855
michael@0 856 if (ch == uint32_t(-1)) {
michael@0 857 deletedCharsArray.AppendElement(true);
michael@0 858 mergeNeeded = true;
michael@0 859 } else {
michael@0 860 deletedCharsArray.AppendElement(false);
michael@0 861 charsToMergeArray.AppendElement(false);
michael@0 862 styleArray.AppendElement(styleContext);
michael@0 863 canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
michael@0 864
michael@0 865 if (IS_IN_BMP(ch)) {
michael@0 866 convertedString.Append(ch);
michael@0 867 } else {
michael@0 868 convertedString.Append(H_SURROGATE(ch));
michael@0 869 convertedString.Append(L_SURROGATE(ch));
michael@0 870 ++i;
michael@0 871 deletedCharsArray.AppendElement(true); // not exactly deleted, but the
michael@0 872 // trailing surrogate is skipped
michael@0 873 ++extraChars;
michael@0 874 }
michael@0 875
michael@0 876 while (extraChars-- > 0) {
michael@0 877 mergeNeeded = true;
michael@0 878 charsToMergeArray.AppendElement(true);
michael@0 879 styleArray.AppendElement(styleContext);
michael@0 880 canBreakBeforeArray.AppendElement(false);
michael@0 881 }
michael@0 882 }
michael@0 883 }
michael@0 884
michael@0 885 uint32_t flags;
michael@0 886 gfxTextRunFactory::Parameters innerParams =
michael@0 887 GetParametersForInner(aTextRun, &flags, aRefContext);
michael@0 888 gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
michael@0 889
michael@0 890 nsAutoPtr<nsTransformedTextRun> transformedChild;
michael@0 891 nsAutoPtr<gfxTextRun> cachedChild;
michael@0 892 gfxTextRun* child;
michael@0 893
michael@0 894 if (mInnerTransformingTextRunFactory) {
michael@0 895 transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
michael@0 896 convertedString.BeginReading(), convertedString.Length(),
michael@0 897 &innerParams, fontGroup, flags, styleArray.Elements(), false);
michael@0 898 child = transformedChild.get();
michael@0 899 } else {
michael@0 900 cachedChild = fontGroup->MakeTextRun(
michael@0 901 convertedString.BeginReading(), convertedString.Length(),
michael@0 902 &innerParams, flags);
michael@0 903 child = cachedChild.get();
michael@0 904 }
michael@0 905 if (!child)
michael@0 906 return;
michael@0 907 // Copy potential linebreaks into child so they're preserved
michael@0 908 // (and also child will be shaped appropriately)
michael@0 909 NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
michael@0 910 "Dropped characters or break-before values somewhere!");
michael@0 911 child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
michael@0 912 canBreakBeforeArray.Elements(), aRefContext);
michael@0 913 if (transformedChild) {
michael@0 914 transformedChild->FinishSettingProperties(aRefContext);
michael@0 915 }
michael@0 916
michael@0 917 if (mergeNeeded) {
michael@0 918 // Now merge multiple characters into one multi-glyph character as required
michael@0 919 // and deal with skipping deleted accent chars
michael@0 920 NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(),
michael@0 921 "source length mismatch");
michael@0 922 NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(),
michael@0 923 "destination length mismatch");
michael@0 924 MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(),
michael@0 925 deletedCharsArray.Elements());
michael@0 926 } else {
michael@0 927 // No merging to do, so just copy; this produces a more optimized textrun.
michael@0 928 // We can't steal the data because the child may be cached and stealing
michael@0 929 // the data would break the cache.
michael@0 930 aTextRun->ResetGlyphRuns();
michael@0 931 aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), 0);
michael@0 932 }
michael@0 933 }

mercurial