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)
24c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)#include "core/rendering/TextRunConstructor.h"
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
26c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink {
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)
5809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)float RenderCombineText::width(unsigned from, unsigned length, const Font& font, float xPosition, TextDirection direction, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
60a9984bf9ddc3cf73fdae3f29134a2bab379e7029Ben Murdoch    if (!length)
61a9984bf9ddc3cf73fdae3f29134a2bab379e7029Ben Murdoch        return 0;
62a9984bf9ddc3cf73fdae3f29134a2bab379e7029Ben Murdoch
630019e4eead4d990e4304c54a9028aca9122fb256Ben Murdoch    if (hasEmptyText())
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined)
6709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        return font.fontDescription().computedSize();
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    return RenderText::width(from, length, font, xPosition, direction, fallbackFonts, glyphOverflow);
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderCombineText::adjustTextOrigin(FloatPoint& textOrigin, const FloatRect& boxRect) const
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined)
7509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        textOrigin.move(boxRect.height() / 2 - ceilf(m_combinedTextWidth) / 2, style()->font().fontDescription().computedPixelSize());
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochvoid RenderCombineText::getStringToRender(int start, StringView& string, int& length) const
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    ASSERT(start >= 0);
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined) {
82d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        string = StringView(m_renderingText.impl());
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        length = string.length();
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    string = text().createView(start, length);
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void RenderCombineText::combineText()
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!m_needsFontUpdate)
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_isCombined = false;
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_needsFontUpdate = false;
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // CSS3 spec says text-combine works only in vertical writing mode.
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (style()->isHorizontalWritingMode())
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
102c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    TextRun run = constructTextRun(this, originalFont(), this, style(), style()->direction());
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    FontDescription description = originalFont().fontDescription();
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float emWidth = description.computedSize() * textCombineMargin;
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    bool shouldUpdateFont = false;
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    description.setOrientation(Horizontal); // We are going to draw combined text horizontally.
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_combinedTextWidth = originalFont().width(run);
1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_isCombined = m_combinedTextWidth <= emWidth;
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    FontSelector* fontSelector = style()->font().fontSelector();
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined)
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        shouldUpdateFont = style()->setFontDescription(description); // Need to change font orientation to horizontal.
1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    else {
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        // Need to try compressed glyphs.
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        static const FontWidthVariant widthVariants[] = { HalfWidth, ThirdWidth, QuarterWidth };
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for (size_t i = 0 ; i < WTF_ARRAY_LENGTH(widthVariants) ; ++i) {
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            description.setWidthVariant(widthVariants[i]);
12009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            Font compressedFont = Font(description);
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            compressedFont.update(fontSelector);
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            float runWidth = compressedFont.width(run);
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (runWidth <= emWidth) {
1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_combinedTextWidth = runWidth;
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_isCombined = true;
1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                // Replace my font with the new one.
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                shouldUpdateFont = style()->setFontDescription(description);
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                break;
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!m_isCombined)
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        shouldUpdateFont = style()->setFontDescription(originalFont().fontDescription());
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (shouldUpdateFont)
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        style()->font().update(fontSelector);
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_isCombined) {
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        DEFINE_STATIC_LOCAL(String, objectReplacementCharacterString, (&objectReplacementCharacter, 1));
142d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        m_renderingText = text();
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        RenderText::setTextInternal(objectReplacementCharacterString.impl());
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
147c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)} // namespace blink
148