void nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, gfxContext* aRefContext) { gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); gfxFontStyle fontStyle = *fontGroup->GetStyle(); fontStyle.size *= 0.8; nsRefPtr<gfxFontGroup> smallFont = fontGroup->Copy(&fontStyle); if (!smallFont) return; PRUint32 flags; gfxTextRunFactory::Parameters innerParams = GetParametersForInner(aTextRun, &flags, aRefContext); PRUint32 length = aTextRun->GetLength(); const PRUnichar* str = aTextRun->mString.BeginReading(); nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements(); // Create a textrun so we can check cluster-start properties nsAutoPtr<gfxTextRun> inner(fontGroup->MakeTextRun(str, length, &innerParams, flags)); if (!inner.get()) return; nsCaseTransformTextRunFactory uppercaseFactory(nsnull, true); aTextRun->ResetGlyphRuns(); PRUint32 runStart = 0; bool runIsLowercase = false; nsAutoTArray<nsStyleContext*,50> styleArray; nsAutoTArray<PRUint8,50> canBreakBeforeArray; PRUint32 i; for (i = 0; i <= length; ++i) { bool isLowercase = false; if (i < length) { // Characters that aren't the start of a cluster are ignored here. They // get added to whatever lowercase/non-lowercase run we're in. if (!inner->IsClusterStart(i)) { isLowercase = runIsLowercase; } else { if (styles[i]->GetStyleFont()->mFont.variant == NS_STYLE_FONT_VARIANT_SMALL_CAPS) { PRUnichar ch = str[i]; PRUnichar ch2; ch2 = ToUpperCase(ch); isLowercase = ch != ch2 || ch == SZLIG; } else { // Don't transform the character! I.e., pretend that it's not lowercase } } } if ((i == length || runIsLowercase != isLowercase) && runStart < i) { nsAutoPtr<nsTransformedTextRun> transformedChild; nsAutoPtr<gfxTextRun> cachedChild; gfxTextRun* child; if (runIsLowercase) { transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart, &innerParams, smallFont, flags, styleArray.Elements(), false); child = transformedChild; } else { cachedChild = fontGroup->MakeTextRun(str + runStart, i - runStart, &innerParams, flags); child = cachedChild.get(); } if (!child) return; // Copy potential linebreaks into child so they're preserved // (and also child will be shaped appropriately) NS_ASSERTION(canBreakBeforeArray.Length() == i - runStart, "lost some break-before values?"); child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), canBreakBeforeArray.Elements(), aRefContext); if (transformedChild) { transformedChild->FinishSettingProperties(aRefContext); } aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), runStart); runStart = i; styleArray.Clear(); canBreakBeforeArray.Clear(); } if (i < length) { runIsLowercase = isLowercase; styleArray.AppendElement(styles[i]); canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); } } }
void nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, gfxContext* aRefContext) { gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); gfxFontStyle fontStyle = *fontGroup->GetStyle(); fontStyle.size *= 0.8; nsRefPtr<gfxFontGroup> smallFont = fontGroup->Copy(&fontStyle); if (!smallFont) return; uint32_t flags; gfxTextRunFactory::Parameters innerParams = GetParametersForInner(aTextRun, &flags, aRefContext); uint32_t length = aTextRun->GetLength(); const PRUnichar* str = aTextRun->mString.BeginReading(); nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements(); // Create a textrun so we can check cluster-start properties nsAutoPtr<gfxTextRun> inner(fontGroup->MakeTextRun(str, length, &innerParams, flags)); if (!inner.get()) return; nsCaseTransformTextRunFactory uppercaseFactory(nullptr, true); aTextRun->ResetGlyphRuns(); uint32_t runStart = 0; nsAutoTArray<nsStyleContext*,50> styleArray; nsAutoTArray<uint8_t,50> canBreakBeforeArray; enum RunCaseState { kUpperOrCaseless, // will be untouched by font-variant:small-caps kLowercase, // will be uppercased and reduced kSpecialUpper // specials: don't shrink, but apply uppercase mapping }; RunCaseState runCase = kUpperOrCaseless; // Note that this loop runs from 0 to length *inclusive*, so the last // iteration is in effect beyond the end of the input text, to give a // chance to finish the last casing run we've found. // The last iteration, when i==length, must not attempt to look at the // character position [i] or the style data for styles[i], as this would // be beyond the valid length of the textrun or its style array. for (uint32_t i = 0; i <= length; ++i) { RunCaseState chCase = kUpperOrCaseless; // Unless we're at the end, figure out what treatment the current // character will need. if (i < length) { nsStyleContext* styleContext = styles[i]; // Characters that aren't the start of a cluster are ignored here. They // get added to whatever lowercase/non-lowercase run we're in. if (!inner->IsClusterStart(i)) { chCase = runCase; } else { if (styleContext->StyleFont()->mFont.variant == NS_STYLE_FONT_VARIANT_SMALL_CAPS) { uint32_t ch = str[i]; if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 && NS_IS_LOW_SURROGATE(str[i + 1])) { ch = SURROGATE_TO_UCS4(ch, str[i + 1]); } uint32_t ch2 = ToUpperCase(ch); if (ch != ch2 || mozilla::unicode::SpecialUpper(ch)) { chCase = kLowercase; } else if (styleContext->StyleFont()->mLanguage == nsGkAtoms::el) { // In Greek, check for characters that will be modified by the // GreekUpperCase mapping - this catches accented capitals where // the accent is to be removed (bug 307039). These are handled by // a transformed child run using the full-size font. GreekCasingState state = kStart; // don't need exact context here ch2 = GreekUpperCase(ch, &state); if (ch != ch2) { chCase = kSpecialUpper; } } } else { // Don't transform the character! I.e., pretend that it's not lowercase } } } // At the end of the text, or when the current character needs different // casing treatment from the current run, finish the run-in-progress // and prepare to accumulate a new run. // Note that we do not look at any source data for offset [i] here, // as that would be invalid in the case where i==length. if ((i == length || runCase != chCase) && runStart < i) { nsAutoPtr<nsTransformedTextRun> transformedChild; nsAutoPtr<gfxTextRun> cachedChild; gfxTextRun* child; switch (runCase) { case kUpperOrCaseless: cachedChild = fontGroup->MakeTextRun(str + runStart, i - runStart, &innerParams, flags); child = cachedChild.get(); break; case kLowercase: transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart, &innerParams, smallFont, flags, styleArray.Elements(), false); child = transformedChild; break; case kSpecialUpper: transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart, &innerParams, fontGroup, flags, styleArray.Elements(), false); child = transformedChild; break; } if (!child) return; // Copy potential linebreaks into child so they're preserved // (and also child will be shaped appropriately) NS_ASSERTION(canBreakBeforeArray.Length() == i - runStart, "lost some break-before values?"); child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), canBreakBeforeArray.Elements(), aRefContext); if (transformedChild) { transformedChild->FinishSettingProperties(aRefContext); } aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), runStart); runStart = i; styleArray.Clear(); canBreakBeforeArray.Clear(); } if (i < length) { runCase = chCase; styleArray.AppendElement(styles[i]); canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); } } }