• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Holger Hans Peter Freyther
4  * Copyright (C) 2014 Google Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "config.h"
24 #include "platform/fonts/WidthIterator.h"
25 
26 #include "platform/fonts/Character.h"
27 #include "platform/fonts/Font.h"
28 #include "platform/fonts/FontPlatformFeatures.h"
29 #include "platform/fonts/GlyphBuffer.h"
30 #include "platform/fonts/Latin1TextIterator.h"
31 #include "platform/fonts/SimpleFontData.h"
32 #include "platform/text/SurrogatePairAwareTextIterator.h"
33 #include "wtf/MathExtras.h"
34 
35 using namespace WTF;
36 using namespace Unicode;
37 
38 namespace blink {
39 
WidthIterator(const Font * font,const TextRun & run,HashSet<const SimpleFontData * > * fallbackFonts,bool accountForGlyphBounds,bool forTextEmphasis)40 WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
41     : m_font(font)
42     , m_run(run)
43     , m_currentCharacter(0)
44     , m_runWidthSoFar(0)
45     , m_isAfterExpansion(!run.allowsLeadingExpansion())
46     , m_fallbackFonts(fallbackFonts)
47     , m_maxGlyphBoundingBoxY(std::numeric_limits<float>::min())
48     , m_minGlyphBoundingBoxY(std::numeric_limits<float>::max())
49     , m_firstGlyphOverflow(0)
50     , m_lastGlyphOverflow(0)
51     , m_accountForGlyphBounds(accountForGlyphBounds)
52     , m_forTextEmphasis(forTextEmphasis)
53 {
54     // If the padding is non-zero, count the number of spaces in the run
55     // and divide that by the padding for per space addition.
56     m_expansion = m_run.expansion();
57     if (!m_expansion)
58         m_expansionPerOpportunity = 0;
59     else {
60         bool isAfterExpansion = m_isAfterExpansion;
61         unsigned expansionOpportunityCount = m_run.is8Bit() ? Character::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.direction(), isAfterExpansion) : Character::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.direction(), isAfterExpansion);
62         if (isAfterExpansion && !m_run.allowsTrailingExpansion())
63             expansionOpportunityCount--;
64 
65         if (!expansionOpportunityCount)
66             m_expansionPerOpportunity = 0;
67         else
68             m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
69     }
70 }
71 
glyphDataForCharacter(CharacterData & charData,bool normalizeSpace)72 GlyphData WidthIterator::glyphDataForCharacter(CharacterData& charData, bool normalizeSpace)
73 {
74     ASSERT(m_font);
75 
76 #if ENABLE(SVG_FONTS)
77     if (TextRun::RenderingContext* renderingContext = m_run.renderingContext()) {
78         return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, charData.character,
79             m_run.rtl(), charData.characterOffset, charData.clusterLength);
80     }
81 #endif
82 
83     return m_font->glyphDataForCharacter(charData.character, m_run.rtl(), normalizeSpace);
84 }
85 
characterWidth(UChar32 character,const GlyphData & glyphData) const86 float WidthIterator::characterWidth(UChar32 character, const GlyphData& glyphData) const
87 {
88     const SimpleFontData* fontData = glyphData.fontData;
89     ASSERT(fontData);
90 
91     if (UNLIKELY(character == '\t' && m_run.allowTabs()))
92         return m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar);
93 
94     float width = fontData->widthForGlyph(glyphData.glyph);
95 
96     // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
97     if (UNLIKELY(m_run.horizontalGlyphStretch() != 1))
98         width *= m_run.horizontalGlyphStretch();
99 
100     return width;
101 }
102 
cacheFallbackFont(const SimpleFontData * fontData,const SimpleFontData * primaryFont)103 void WidthIterator::cacheFallbackFont(const SimpleFontData* fontData,
104     const SimpleFontData* primaryFont)
105 {
106     if (fontData == primaryFont)
107         return;
108 
109     m_fallbackFonts->add(fontData);
110 }
111 
adjustSpacing(float width,const CharacterData & charData,const SimpleFontData & fontData,GlyphBuffer * glyphBuffer)112 float WidthIterator::adjustSpacing(float width, const CharacterData& charData,
113     const SimpleFontData& fontData, GlyphBuffer* glyphBuffer)
114 {
115     // Account for letter-spacing.
116     if (width)
117         width += m_font->fontDescription().letterSpacing();
118 
119     static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
120     bool treatAsSpace = Character::treatAsSpace(charData.character);
121     if (treatAsSpace || (expandAroundIdeographs && Character::isCJKIdeographOrSymbol(charData.character))) {
122         // Distribute the run's total expansion evenly over all expansion opportunities in the run.
123         if (m_expansion) {
124             if (!treatAsSpace && !m_isAfterExpansion) {
125                 // Take the expansion opportunity before this ideograph.
126                 m_expansion -= m_expansionPerOpportunity;
127                 float expansionAtThisOpportunity = m_expansionPerOpportunity;
128                 m_runWidthSoFar += expansionAtThisOpportunity;
129                 if (glyphBuffer) {
130                     if (glyphBuffer->isEmpty()) {
131                         if (m_forTextEmphasis)
132                             glyphBuffer->add(fontData.zeroWidthSpaceGlyph(), &fontData, m_expansionPerOpportunity);
133                         else
134                             glyphBuffer->add(fontData.spaceGlyph(), &fontData, expansionAtThisOpportunity);
135                     } else {
136                         glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
137                     }
138                 }
139             }
140             if (m_run.allowsTrailingExpansion()
141                 || (m_run.ltr() && charData.characterOffset + charData.clusterLength < static_cast<size_t>(m_run.length()))
142                 || (m_run.rtl() && charData.characterOffset)) {
143                 m_expansion -= m_expansionPerOpportunity;
144                 width += m_expansionPerOpportunity;
145                 m_isAfterExpansion = true;
146             }
147         } else {
148             m_isAfterExpansion = false;
149         }
150 
151         // Account for word spacing.
152         // We apply additional space between "words" by adding width to the space character.
153         if (treatAsSpace && (charData.character != '\t' || !m_run.allowTabs())
154             && (charData.characterOffset || charData.character == noBreakSpace)
155             && m_font->fontDescription().wordSpacing()) {
156             width += m_font->fontDescription().wordSpacing();
157         }
158     } else {
159         m_isAfterExpansion = false;
160     }
161 
162     return width;
163 }
164 
updateGlyphBounds(const GlyphData & glyphData,float width,bool firstCharacter)165 void WidthIterator::updateGlyphBounds(const GlyphData& glyphData, float width, bool firstCharacter)
166 {
167     ASSERT(glyphData.fontData);
168     FloatRect bounds = glyphData.fontData->boundsForGlyph(glyphData.glyph);
169 
170     if (firstCharacter)
171         m_firstGlyphOverflow = std::max<float>(0, -bounds.x());
172     m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
173     m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY());
174     m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
175 }
176 
177 template <typename TextIterator>
advanceInternal(TextIterator & textIterator,GlyphBuffer * glyphBuffer)178 unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
179 {
180     bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion)
181         && !m_run.spacingDisabled();
182 
183     const SimpleFontData* primaryFont = m_font->primaryFont();
184     const SimpleFontData* lastFontData = primaryFont;
185     bool normalizeSpace = m_run.normalizeSpace();
186 
187     CharacterData charData;
188     while (textIterator.consume(charData.character, charData.clusterLength)) {
189         charData.characterOffset = textIterator.currentCharacter();
190 
191         GlyphData glyphData = glyphDataForCharacter(charData, normalizeSpace);
192 
193         // Some fonts do not have a glyph for zero-width-space,
194         // in that case use the space character and override the width.
195         float width;
196         if (!glyphData.glyph && Character::treatAsZeroWidthSpaceInComplexScript(charData.character)) {
197             charData.character = space;
198             glyphData = glyphDataForCharacter(charData);
199             width = 0;
200         } else {
201             width = characterWidth(charData.character, glyphData);
202         }
203 
204         Glyph glyph = glyphData.glyph;
205         const SimpleFontData* fontData = glyphData.fontData;
206         ASSERT(fontData);
207 
208         if (m_fallbackFonts && lastFontData != fontData && width) {
209             lastFontData = fontData;
210             cacheFallbackFont(fontData, primaryFont);
211         }
212 
213         if (hasExtraSpacing)
214             width = adjustSpacing(width, charData, *fontData, glyphBuffer);
215 
216         if (m_accountForGlyphBounds)
217             updateGlyphBounds(glyphData, width, !charData.characterOffset);
218 
219         if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(charData.character))
220             glyph = 0;
221 
222         // Advance past the character we just dealt with.
223         textIterator.advance(charData.clusterLength);
224         m_runWidthSoFar += width;
225 
226         if (glyphBuffer)
227             glyphBuffer->add(glyph, fontData, width);
228     }
229 
230     unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
231     m_currentCharacter = textIterator.currentCharacter();
232 
233     return consumedCharacters;
234 }
235 
advance(int offset,GlyphBuffer * glyphBuffer)236 unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
237 {
238     int length = m_run.length();
239 
240     if (offset > length)
241         offset = length;
242 
243     if (m_currentCharacter >= static_cast<unsigned>(offset))
244         return 0;
245 
246     if (m_run.is8Bit()) {
247         Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
248         return advanceInternal(textIterator, glyphBuffer);
249     }
250 
251     SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
252     return advanceInternal(textIterator, glyphBuffer);
253 }
254 
advanceOneCharacter(float & width)255 bool WidthIterator::advanceOneCharacter(float& width)
256 {
257     float initialWidth = m_runWidthSoFar;
258 
259     if (!advance(m_currentCharacter + 1))
260         return false;
261 
262     width = m_runWidthSoFar - initialWidth;
263     return true;
264 }
265 
266 } // namespace blink
267