15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2011 Apple Inc. All rights reserved.
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * This library is free software; you can redistribute it and/or
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modify it under the terms of the GNU Library General Public
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * License as published by the Free Software Foundation; either
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * version 2 of the License, or (at your option) any later version.
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * This library is distributed in the hope that it will be useful,
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * but WITHOUT ANY WARRANTY; without even the implied warranty of
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Library General Public License for more details.
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * You should have received a copy of the GNU Library General Public License
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * along with this library; see the file COPYING.LIB.  If not, write to
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Boston, MA 02110-1301, USA.
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h"
2253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/rendering/RenderCombineText.h"
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2419cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)#include "core/rendering/RenderBlockFlow.h"
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)namespace WebCore {
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)const float textCombineMargin = 1.1f; // Allow em + 10% margin
295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)RenderCombineText::RenderCombineText(Node* node, PassRefPtr<StringImpl> string)
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     : RenderText(node, string)
325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     , m_combinedTextWidth(0)
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     , m_isCombined(false)
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)     , m_needsFontUpdate(false)
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderCombineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    setStyleInternal(RenderStyle::clone(style()));
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    RenderText::styleDidChange(diff, oldStyle);
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined) {
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        RenderText::setTextInternal(originalText()); // This RenderCombineText has been combined once. Restore the original text for the next combineText().
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_isCombined = false;
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_needsFontUpdate = true;
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderCombineText::setTextInternal(PassRefPtr<StringImpl> text)
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    RenderText::setTextInternal(text);
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_needsFontUpdate = true;
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)float RenderCombineText::width(unsigned from, unsigned length, const Font& font, float xPosition, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
600019e4eead4d990e4304c54a9028aca9122fb256Ben Murdoch    if (hasEmptyText())
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined)
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return font.size();
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return RenderText::width(from, length, font, xPosition, fallbackFonts, glyphOverflow);
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderCombineText::adjustTextOrigin(FloatPoint& textOrigin, const FloatRect& boxRect) const
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined)
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        textOrigin.move(boxRect.height() / 2 - ceilf(m_combinedTextWidth) / 2, style()->font().pixelSize());
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochvoid RenderCombineText::getStringToRender(int start, StringView& string, int& length) const
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    ASSERT(start >= 0);
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined) {
797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        string = StringView(originalText());
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        length = string.length();
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    string = text().createView(start, length);
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderCombineText::combineText()
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!m_needsFontUpdate)
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_isCombined = false;
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_needsFontUpdate = false;
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // CSS3 spec says text-combine works only in vertical writing mode.
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (style()->isHorizontalWritingMode())
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
9919cde67944066db31e633d9e386f2aa9bf9fadb3Torne (Richard Coles)    TextRun run = RenderBlockFlow::constructTextRun(this, originalFont(), this, style());
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    FontDescription description = originalFont().fontDescription();
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float emWidth = description.computedSize() * textCombineMargin;
1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    bool shouldUpdateFont = false;
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    description.setOrientation(Horizontal); // We are going to draw combined text horizontally.
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_combinedTextWidth = originalFont().width(run);
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_isCombined = m_combinedTextWidth <= emWidth;
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    FontSelector* fontSelector = style()->font().fontSelector();
1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined)
1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        shouldUpdateFont = style()->setFontDescription(description); // Need to change font orientation to horizontal.
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    else {
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        // Need to try compressed glyphs.
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        static const FontWidthVariant widthVariants[] = { HalfWidth, ThirdWidth, QuarterWidth };
1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for (size_t i = 0 ; i < WTF_ARRAY_LENGTH(widthVariants) ; ++i) {
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            description.setWidthVariant(widthVariants[i]);
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            Font compressedFont = Font(description, style()->font().letterSpacing(), style()->font().wordSpacing());
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            compressedFont.update(fontSelector);
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            float runWidth = compressedFont.width(run);
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (runWidth <= emWidth) {
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_combinedTextWidth = runWidth;
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_isCombined = true;
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                // Replace my font with the new one.
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                shouldUpdateFont = style()->setFontDescription(description);
1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                break;
1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!m_isCombined)
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        shouldUpdateFont = style()->setFontDescription(originalFont().fontDescription());
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (shouldUpdateFont)
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        style()->font().update(fontSelector);
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined) {
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        DEFINE_STATIC_LOCAL(String, objectReplacementCharacterString, (&objectReplacementCharacter, 1));
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        RenderText::setTextInternal(objectReplacementCharacterString.impl());
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} // namespace WebCore
144