Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2011 Apple Inc. All rights reserved.
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  *
     19  */
     20 
     21 #include "config.h"
     22 #include "RenderCombineText.h"
     23 
     24 #include "TextRun.h"
     25 
     26 namespace WebCore {
     27 
     28 const float textCombineMargin = 1.1f; // Allow em + 10% margin
     29 
     30 RenderCombineText::RenderCombineText(Node* node, PassRefPtr<StringImpl> string)
     31      : RenderText(node, string)
     32      , m_combinedTextWidth(0)
     33      , m_isCombined(false)
     34      , m_needsFontUpdate(false)
     35 {
     36 }
     37 
     38 void RenderCombineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     39 {
     40     setStyleInternal(RenderStyle::clone(style()));
     41     RenderText::styleDidChange(diff, oldStyle);
     42 
     43     if (m_isCombined)
     44         RenderText::setTextInternal(originalText()); // This RenderCombineText has been combined once. Restore the original text for the next combineText().
     45 
     46     m_needsFontUpdate = true;
     47 }
     48 
     49 void RenderCombineText::setTextInternal(PassRefPtr<StringImpl> text)
     50 {
     51     RenderText::setTextInternal(text);
     52 
     53     m_needsFontUpdate = true;
     54 }
     55 
     56 float RenderCombineText::width(unsigned from, unsigned length, const Font& font, float xPosition, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
     57 {
     58     if (!characters())
     59         return 0;
     60 
     61     if (m_isCombined)
     62         return font.size();
     63 
     64     return RenderText::width(from, length, font, xPosition, fallbackFonts, glyphOverflow);
     65 }
     66 
     67 void RenderCombineText::adjustTextOrigin(FloatPoint& textOrigin, const FloatRect& boxRect) const
     68 {
     69     if (m_isCombined)
     70         textOrigin.move(boxRect.height() / 2 - ceilf(m_combinedTextWidth) / 2, style()->font().pixelSize());
     71 }
     72 
     73 void RenderCombineText::charactersToRender(int start, const UChar*& characters, int& length) const
     74 {
     75     if (m_isCombined) {
     76         length = originalText()->length();
     77         characters = originalText()->characters();
     78         return;
     79     }
     80 
     81     characters = text()->characters() + start;
     82 }
     83 
     84 void RenderCombineText::combineText()
     85 {
     86     if (!m_needsFontUpdate)
     87         return;
     88 
     89     m_isCombined = false;
     90     m_needsFontUpdate = false;
     91 
     92     // CSS3 spec says text-combine works only in vertical writing mode.
     93     if (style()->isHorizontalWritingMode())
     94         return;
     95 
     96     TextRun run = TextRun(String(text()));
     97     FontDescription description = originalFont().fontDescription();
     98     float emWidth = description.computedSize() * textCombineMargin;
     99     bool shouldUpdateFont = false;
    100 
    101     description.setOrientation(Horizontal); // We are going to draw combined text horizontally.
    102     m_combinedTextWidth = originalFont().width(run);
    103     m_isCombined = m_combinedTextWidth <= emWidth;
    104 
    105     if (m_isCombined)
    106         shouldUpdateFont = style()->setFontDescription(description); // Need to change font orientation to horizontal.
    107     else {
    108         // Need to try compressed glyphs.
    109         static const FontWidthVariant widthVariants[] = { HalfWidth, ThirdWidth, QuarterWidth };
    110         for (size_t i = 0 ; i < WTF_ARRAY_LENGTH(widthVariants) ; ++i) {
    111             description.setWidthVariant(widthVariants[i]);
    112             Font compressedFont = Font(description, style()->font().letterSpacing(), style()->font().wordSpacing());
    113             compressedFont.update(style()->font().fontSelector());
    114             float runWidth = compressedFont.width(run);
    115             if (runWidth <= emWidth) {
    116                 m_combinedTextWidth = runWidth;
    117                 m_isCombined = true;
    118 
    119                 // Replace my font with the new one.
    120                 shouldUpdateFont = style()->setFontDescription(description);
    121                 break;
    122             }
    123         }
    124     }
    125 
    126     if (!m_isCombined)
    127         shouldUpdateFont = style()->setFontDescription(originalFont().fontDescription());
    128 
    129     if (shouldUpdateFont)
    130         style()->font().update(style()->font().fontSelector());
    131 
    132     if (m_isCombined) {
    133         DEFINE_STATIC_LOCAL(String, objectReplacementCharacterString, (&objectReplacementCharacter, 1));
    134         RenderText::setTextInternal(objectReplacementCharacterString.impl());
    135     }
    136 }
    137 
    138 } // namespace WebCore
    139