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