diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderCombineText.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderCombineText.cpp | 123 |
1 files changed, 87 insertions, 36 deletions
diff --git a/Source/WebCore/rendering/RenderCombineText.cpp b/Source/WebCore/rendering/RenderCombineText.cpp index dd4fd317b..126519f15 100644 --- a/Source/WebCore/rendering/RenderCombineText.cpp +++ b/Source/WebCore/rendering/RenderCombineText.cpp @@ -23,14 +23,14 @@ #include "RenderBlock.h" #include "StyleInheritedData.h" +#include <wtf/NeverDestroyed.h> namespace WebCore { const float textCombineMargin = 1.15f; // Allow em + 15% margin -RenderCombineText::RenderCombineText(Text& textNode, PassRefPtr<StringImpl> string) +RenderCombineText::RenderCombineText(Text& textNode, const String& string) : RenderText(textNode, string) - , m_combinedTextWidth(0) , m_isCombined(false) , m_needsFontUpdate(false) { @@ -39,53 +39,60 @@ RenderCombineText::RenderCombineText(Text& textNode, PassRefPtr<StringImpl> stri void RenderCombineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { // FIXME: This is pretty hackish. - m_combineFontStyle = RenderStyle::clone(&style()); + // Only cache a new font style if our old one actually changed. We do this to avoid + // clobbering width variants and shrink-to-fit changes, since we won't recombine when + // the font doesn't change. + if (!oldStyle || oldStyle->fontCascade() != style().fontCascade()) + m_combineFontStyle = RenderStyle::clonePtr(style()); RenderText::styleDidChange(diff, oldStyle); - if (m_isCombined) { - RenderText::setTextInternal(originalText()); // This RenderCombineText has been combined once. Restore the original text for the next combineText(). + if (m_isCombined && selfNeedsLayout()) { + // Layouts cause the text to be recombined; therefore, only only un-combine when the style diff causes a layout. + RenderText::setRenderedText(originalText()); // This RenderCombineText has been combined once. Restore the original text for the next combineText(). m_isCombined = false; } m_needsFontUpdate = true; } -void RenderCombineText::setTextInternal(const String& text) +void RenderCombineText::setRenderedText(const String& text) { - RenderText::setTextInternal(text); + RenderText::setRenderedText(text); m_needsFontUpdate = true; } -float RenderCombineText::width(unsigned from, unsigned length, const Font& font, float xPosition, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const +float RenderCombineText::width(unsigned from, unsigned length, const FontCascade& font, float xPosition, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const { - if (!deprecatedCharacters()) - return 0; - if (m_isCombined) - return font.size(); + return !length ? 0 : font.size(); return RenderText::width(from, length, font, xPosition, fallbackFonts, glyphOverflow); } -void RenderCombineText::adjustTextOrigin(FloatPoint& textOrigin, const FloatRect& boxRect) const +std::optional<FloatPoint> RenderCombineText::computeTextOrigin(const FloatRect& boxRect) const { - if (m_isCombined) - textOrigin.move(boxRect.height() / 2 - ceilf(m_combinedTextWidth) / 2, style().font().pixelSize()); + if (!m_isCombined) + return std::nullopt; + + // Visually center m_combinedTextWidth/Ascent/Descent within boxRect + FloatPoint result = boxRect.minXMaxYCorner(); + FloatSize combinedTextSize(m_combinedTextWidth, m_combinedTextAscent + m_combinedTextDescent); + result.move((boxRect.size().transposedSize() - combinedTextSize) / 2); + result.move(0, m_combinedTextAscent); + return result; } -void RenderCombineText::getStringToRender(int start, String& string, int& length) const +String RenderCombineText::combinedStringForRendering() const { - ASSERT(start >= 0); if (m_isCombined) { - string = originalText(); - length = string.length(); - return; + auto originalText = this->originalText(); + ASSERT(!originalText.isNull()); + return originalText; } - string = text(); - string = string.substringSharingImpl(static_cast<unsigned>(start), length); + return { }; } void RenderCombineText::combineText() @@ -93,6 +100,10 @@ void RenderCombineText::combineText() if (!m_needsFontUpdate) return; + // An ancestor element may trigger us to lay out again, even when we're already combined. + if (m_isCombined) + RenderText::setRenderedText(originalText()); + m_isCombined = false; m_needsFontUpdate = false; @@ -100,47 +111,87 @@ void RenderCombineText::combineText() if (style().isHorizontalWritingMode()) return; - TextRun run = RenderBlock::constructTextRun(this, originalFont(), this, style()); - FontDescription description = originalFont().fontDescription(); + auto description = originalFont().fontDescription(); float emWidth = description.computedSize() * textCombineMargin; bool shouldUpdateFont = false; + FontSelector* fontSelector = style().fontCascade().fontSelector(); + description.setOrientation(Horizontal); // We are going to draw combined text horizontally. - m_combinedTextWidth = originalFont().width(run); - m_isCombined = m_combinedTextWidth <= emWidth; - FontSelector* fontSelector = style().font().fontSelector(); + FontCascade horizontalFont(description, style().fontCascade().letterSpacing(), style().fontCascade().wordSpacing()); + horizontalFont.update(fontSelector); + + GlyphOverflow glyphOverflow; + glyphOverflow.computeBounds = true; + float combinedTextWidth = width(0, textLength(), horizontalFont, 0, nullptr, &glyphOverflow); + + float bestFitDelta = combinedTextWidth - emWidth; + auto bestFitDescription = description; + m_isCombined = combinedTextWidth <= emWidth; + if (m_isCombined) shouldUpdateFont = m_combineFontStyle->setFontDescription(description); // Need to change font orientation to horizontal. else { // Need to try compressed glyphs. static const FontWidthVariant widthVariants[] = { HalfWidth, ThirdWidth, QuarterWidth }; for (size_t i = 0 ; i < WTF_ARRAY_LENGTH(widthVariants) ; ++i) { - description.setWidthVariant(widthVariants[i]); - Font compressedFont = Font(description, style().font().letterSpacing(), style().font().wordSpacing()); + description.setWidthVariant(widthVariants[i]); // When modifying this, make sure to keep it in sync with FontPlatformData::isForTextCombine()! + + FontCascade compressedFont(description, style().fontCascade().letterSpacing(), style().fontCascade().wordSpacing()); compressedFont.update(fontSelector); - float runWidth = compressedFont.width(run); + + glyphOverflow.left = glyphOverflow.top = glyphOverflow.right = glyphOverflow.bottom = 0; + float runWidth = RenderText::width(0, textLength(), compressedFont, 0, nullptr, &glyphOverflow); if (runWidth <= emWidth) { - m_combinedTextWidth = runWidth; + combinedTextWidth = runWidth; m_isCombined = true; // Replace my font with the new one. shouldUpdateFont = m_combineFontStyle->setFontDescription(description); break; } + + float widthDelta = runWidth - emWidth; + if (widthDelta < bestFitDelta) { + bestFitDelta = widthDelta; + bestFitDescription = description; + } } } - if (!m_isCombined) - shouldUpdateFont = m_combineFontStyle->setFontDescription(originalFont().fontDescription()); + if (!m_isCombined) { + float scaleFactor = std::max(0.4f, emWidth / (emWidth + bestFitDelta)); + float originalSize = bestFitDescription.computedSize(); + do { + float computedSize = originalSize * scaleFactor; + bestFitDescription.setComputedSize(computedSize); + shouldUpdateFont = m_combineFontStyle->setFontDescription(bestFitDescription); + + FontCascade compressedFont(bestFitDescription, style().fontCascade().letterSpacing(), style().fontCascade().wordSpacing()); + compressedFont.update(fontSelector); + + glyphOverflow.left = glyphOverflow.top = glyphOverflow.right = glyphOverflow.bottom = 0; + float runWidth = RenderText::width(0, textLength(), compressedFont, 0, nullptr, &glyphOverflow); + if (runWidth <= emWidth) { + combinedTextWidth = runWidth; + m_isCombined = true; + break; + } + scaleFactor -= 0.05f; + } while (scaleFactor >= 0.4f); + } if (shouldUpdateFont) - m_combineFontStyle->font().update(fontSelector); + m_combineFontStyle->fontCascade().update(fontSelector); if (m_isCombined) { - DEFINE_STATIC_LOCAL(String, objectReplacementCharacterString, (&objectReplacementCharacter, 1)); - RenderText::setTextInternal(objectReplacementCharacterString.impl()); + static NeverDestroyed<String> objectReplacementCharacterString(&objectReplacementCharacter, 1); + RenderText::setRenderedText(objectReplacementCharacterString.get()); + m_combinedTextWidth = combinedTextWidth; + m_combinedTextAscent = glyphOverflow.top; + m_combinedTextDescent = glyphOverflow.bottom; } } |