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