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 "RenderBlock.h" 25#include "StyleInheritedData.h" 26 27namespace WebCore { 28 29const float textCombineMargin = 1.15f; // Allow em + 15% margin 30 31RenderCombineText::RenderCombineText(Node* node, PassRefPtr<StringImpl> string) 32 : RenderText(node, string) 33 , m_combinedTextWidth(0) 34 , m_isCombined(false) 35 , m_needsFontUpdate(false) 36{ 37} 38 39void RenderCombineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 40{ 41 setStyleInternal(RenderStyle::clone(style())); 42 RenderText::styleDidChange(diff, oldStyle); 43 44 if (m_isCombined) { 45 RenderText::setTextInternal(originalText()); // This RenderCombineText has been combined once. Restore the original text for the next combineText(). 46 m_isCombined = false; 47 } 48 49 m_needsFontUpdate = true; 50} 51 52void RenderCombineText::setTextInternal(PassRefPtr<StringImpl> text) 53{ 54 RenderText::setTextInternal(text); 55 56 m_needsFontUpdate = true; 57} 58 59float RenderCombineText::width(unsigned from, unsigned length, const Font& font, float xPosition, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const 60{ 61 if (!characters()) 62 return 0; 63 64 if (m_isCombined) 65 return font.size(); 66 67 return RenderText::width(from, length, font, xPosition, fallbackFonts, glyphOverflow); 68} 69 70void RenderCombineText::adjustTextOrigin(FloatPoint& textOrigin, const FloatRect& boxRect) const 71{ 72 if (m_isCombined) 73 textOrigin.move(boxRect.height() / 2 - ceilf(m_combinedTextWidth) / 2, style()->font().pixelSize()); 74} 75 76void RenderCombineText::getStringToRender(int start, String& string, int& length) const 77{ 78 ASSERT(start >= 0); 79 if (m_isCombined) { 80 string = originalText(); 81 length = string.length(); 82 return; 83 } 84 85 string = text(); 86 string = string.substringSharingImpl(static_cast<unsigned>(start), length); 87} 88 89void RenderCombineText::combineText() 90{ 91 if (!m_needsFontUpdate) 92 return; 93 94 m_isCombined = false; 95 m_needsFontUpdate = false; 96 97 // CSS3 spec says text-combine works only in vertical writing mode. 98 if (style()->isHorizontalWritingMode()) 99 return; 100 101 TextRun run = RenderBlock::constructTextRun(this, originalFont(), this, style()); 102 FontDescription description = originalFont().fontDescription(); 103 float emWidth = description.computedSize() * textCombineMargin; 104 bool shouldUpdateFont = false; 105 106 description.setOrientation(Horizontal); // We are going to draw combined text horizontally. 107 m_combinedTextWidth = originalFont().width(run); 108 m_isCombined = m_combinedTextWidth <= emWidth; 109 110 FontSelector* fontSelector = style()->font().fontSelector(); 111 112 if (m_isCombined) 113 shouldUpdateFont = style()->setFontDescription(description); // Need to change font orientation to horizontal. 114 else { 115 // Need to try compressed glyphs. 116 static const FontWidthVariant widthVariants[] = { HalfWidth, ThirdWidth, QuarterWidth }; 117 for (size_t i = 0 ; i < WTF_ARRAY_LENGTH(widthVariants) ; ++i) { 118 description.setWidthVariant(widthVariants[i]); 119 Font compressedFont = Font(description, style()->font().letterSpacing(), style()->font().wordSpacing()); 120 compressedFont.update(fontSelector); 121 float runWidth = compressedFont.width(run); 122 if (runWidth <= emWidth) { 123 m_combinedTextWidth = runWidth; 124 m_isCombined = true; 125 126 // Replace my font with the new one. 127 shouldUpdateFont = style()->setFontDescription(description); 128 break; 129 } 130 } 131 } 132 133 if (!m_isCombined) 134 shouldUpdateFont = style()->setFontDescription(originalFont().fontDescription()); 135 136 if (shouldUpdateFont) 137 style()->font().update(fontSelector); 138 139 if (m_isCombined) { 140 DEFINE_STATIC_LOCAL(String, objectReplacementCharacterString, (&objectReplacementCharacter, 1)); 141 RenderText::setTextInternal(objectReplacementCharacterString.impl()); 142 } 143} 144 145} // namespace WebCore 146