1/*
2 * Copyright (c) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "HarfBuzzShaper.h"
33
34#include "Font.h"
35#include "SurrogatePairAwareTextIterator.h"
36#include "TextRun.h"
37#include "hb-icu.h"
38#include <unicode/normlzr.h>
39#include <unicode/uchar.h>
40#include <wtf/MathExtras.h>
41#include <wtf/Vector.h>
42#include <wtf/unicode/Unicode.h>
43
44namespace WebCore {
45
46static inline float harfbuzzPositionToFloat(hb_position_t value)
47{
48 return static_cast<float>(value) / (1 << 16);
49}
50
51HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(unsigned numCharacters, TextDirection direction, hb_buffer_t* harfbuzzBuffer)
52 : m_numCharacters(numCharacters)
53 , m_direction(direction)
54{
55 m_numGlyphs = hb_buffer_get_length(harfbuzzBuffer);
56 m_glyphs.resize(m_numGlyphs);
57 m_advances.resize(m_numGlyphs);
58 m_offsets.resize(m_numGlyphs);
59 m_glyphToCharacterIndex.resize(m_numGlyphs);
60 m_logClusters.resize(m_numCharacters);
61
62 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(harfbuzzBuffer, 0);
63 for (unsigned i = 0; i < m_numGlyphs; ++i)
64 m_glyphToCharacterIndex[i] = infos[i].cluster;
65
66 // Fill logical clusters
67 unsigned index = 0;
68 while (index < m_numGlyphs) {
69 unsigned nextIndex = index + 1;
70 while (nextIndex < m_numGlyphs && infos[index].cluster == infos[nextIndex].cluster)
71 ++nextIndex;
72 if (rtl()) {
73 int nextCluster = nextIndex < m_numGlyphs ? infos[nextIndex].cluster : -1;
74 for (int j = infos[index].cluster; j > nextCluster; --j)
75 m_logClusters[j] = index;
76 } else {
77 unsigned nextCluster = nextIndex < m_numGlyphs ? infos[nextIndex].cluster : m_numCharacters;
78 for (unsigned j = infos[index].cluster; j < nextCluster; ++j)
79 m_logClusters[j] = index;
80 }
81 index = nextIndex;
82 }
83}
84
85void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float x, float y, float advance)
86{
87 m_glyphs[index] = glyphId;
88 m_offsets[index].set(x, y);
89 m_advances[index] = advance;
90}
91
92int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(int targetX)
93{
94 ASSERT(static_cast<unsigned>(targetX) <= m_width);
95 int currentX = 0;
96 float prevAdvance = 0;
97 for (unsigned i = 0; i < m_numGlyphs; ++i) {
98 float currentAdvance = m_advances[i] / 2.0;
99 int nextX = currentX + roundf(prevAdvance + currentAdvance);
100 if (currentX <= targetX && targetX <= nextX)
101 return m_glyphToCharacterIndex[i] + (rtl() ? 1 : 0);
102 currentX = nextX;
103 prevAdvance = currentAdvance;
104 }
105
106 return rtl() ? 0 : m_numCharacters;
107}
108
109int HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset)
110{
111 ASSERT(offset < m_numCharacters);
112 unsigned glyphIndex = m_logClusters[offset];
113 ASSERT(glyphIndex < m_numGlyphs);
114 float position = m_offsets[glyphIndex].x();
115 if (rtl())
116 position += m_advances[glyphIndex];
117 return roundf(position);
118}
119
120HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run)
121 : HarfBuzzShaperBase(font, run)
122 , m_startIndexOfCurrentRun(0)
123 , m_numCharactersOfCurrentRun(0)
124 , m_harfbuzzBuffer(0)
125{
126 setNormalizedBuffer();
127 setFontFeatures();
128}
129
130HarfBuzzShaper::~HarfBuzzShaper()
131{
132 if (m_harfbuzzBuffer)
133 hb_buffer_destroy(m_harfbuzzBuffer);
134}
135
136void HarfBuzzShaper::setFontFeatures()
137{
138 FontFeatureSettings* settings = m_font->fontDescription().featureSettings();
139 if (!settings)
140 return;
141
142 unsigned numFeatures = settings->size();
143 m_features.resize(numFeatures);
144 for (unsigned i = 0; i < numFeatures; ++i) {
145 const UChar* tag = settings->at(i).tag().characters();
146 m_features[i].tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);
147 m_features[i].value = settings->at(i).value();
148 m_features[i].start = 0;
149 m_features[i].end = static_cast<unsigned>(-1);
150 }
151}
152
153bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer)
154{
155 m_totalWidth = 0;
156 while (setupHarfBuzzRun()) {
157 if (!shapeHarfBuzzRun())
158 return false;
159 setGlyphPositionsForHarfBuzzRun(glyphBuffer);
160 }
161
162 if (!m_harfbuzzRuns.size())
163 return false;
164
165 return true;
166}
167
168bool HarfBuzzShaper::setupHarfBuzzRun()
169{
170 m_startIndexOfCurrentRun += m_numCharactersOfCurrentRun;
171
172 // Iterate through the text to take the largest range that stays within
173 // a single font.
174 int endOfRunIndex = m_normalizedBufferLength - m_startIndexOfCurrentRun;
175 SurrogatePairAwareTextIterator iterator(m_normalizedBuffer.get() + m_startIndexOfCurrentRun, 0, endOfRunIndex, endOfRunIndex);
176 UChar32 character;
177 unsigned clusterLength = 0;
178 if (!iterator.consume(character, clusterLength))
179 return false;
180
181 m_currentFontData = m_font->glyphDataForCharacter(character, false).fontData;
182 UErrorCode errorCode = U_ZERO_ERROR;
183 UScriptCode currentScript = uscript_getScript(character, &errorCode);
184 if (U_FAILURE(errorCode))
185 return false;
186 if (currentScript == USCRIPT_INHERITED)
187 currentScript = USCRIPT_COMMON;
188 for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) {
189 const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(character, false).fontData;
190 if (nextFontData != m_currentFontData)
191 break;
192 UScriptCode nextScript = uscript_getScript(character, &errorCode);
193 if (U_FAILURE(errorCode))
194 return false;
195 if (currentScript == nextScript || nextScript == USCRIPT_INHERITED || nextScript == USCRIPT_COMMON)
196 continue;
197 if (currentScript == USCRIPT_COMMON)
198 currentScript = nextScript;
199 else
200 break;
201 }
202 m_numCharactersOfCurrentRun = iterator.currentCharacter();
203
204 if (!m_harfbuzzBuffer) {
205 m_harfbuzzBuffer = hb_buffer_create();
206 hb_buffer_set_unicode_funcs(m_harfbuzzBuffer, hb_icu_get_unicode_funcs());
207 } else
208 hb_buffer_reset(m_harfbuzzBuffer);
209 hb_buffer_set_script(m_harfbuzzBuffer, hb_icu_script_to_script(currentScript));
210
211 // WebKit always sets direction to LTR during width calculation.
212 // We only set direction when direction is explicitly set to RTL so that
213 // preventng wrong width calculation.
214 if (m_run.rtl())
215 hb_buffer_set_direction(m_harfbuzzBuffer, HB_DIRECTION_RTL);
216
217 // Determine whether this run needs to be converted to small caps.
218 // nextScriptRun() will always send us a run of the same case, because a
219 // case change while in small-caps mode always results in different
220 // FontData, so we only need to check the first character's case.
221 if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[m_startIndexOfCurrentRun])) {
222 String upperText = String(m_normalizedBuffer.get() + m_startIndexOfCurrentRun, m_numCharactersOfCurrentRun);
223 upperText.makeUpper();
224 m_currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData;
225 hb_buffer_add_utf16(m_harfbuzzBuffer, upperText.characters(), m_numCharactersOfCurrentRun, 0, m_numCharactersOfCurrentRun);
226 } else
227 hb_buffer_add_utf16(m_harfbuzzBuffer, m_normalizedBuffer.get() + m_startIndexOfCurrentRun, m_numCharactersOfCurrentRun, 0, m_numCharactersOfCurrentRun);
228
229 return true;
230}
231
232bool HarfBuzzShaper::shapeHarfBuzzRun()
233{
234 FontPlatformData* platformData = const_cast<FontPlatformData*>(&m_currentFontData->platformData());
235 HarfBuzzFace* face = platformData->harfbuzzFace();
236 if (!face)
237 return false;
238 hb_font_t* harfbuzzFont = face->createFont();
239 hb_shape(harfbuzzFont, m_harfbuzzBuffer, m_features.size() > 0 ? m_features.data() : 0, m_features.size());
240 hb_font_destroy(harfbuzzFont);
241 m_harfbuzzRuns.append(HarfBuzzRun::create(m_numCharactersOfCurrentRun, m_run.direction(), m_harfbuzzBuffer));
242 return true;
243}
244
245void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(GlyphBuffer* glyphBuffer)
246{
247 hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(m_harfbuzzBuffer, 0);
248 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(m_harfbuzzBuffer, 0);
249 HarfBuzzRun* currentRun = m_harfbuzzRuns.last().get();
250
251 unsigned numGlyphs = currentRun->numGlyphs();
252 float totalAdvance = 0;
253 float nextOffsetX = harfbuzzPositionToFloat(glyphPositions[0].x_offset);
254 float nextOffsetY = -harfbuzzPositionToFloat(glyphPositions[0].y_offset);
255 // HarfBuzz returns the shaping result in visual order. We need not to flip them for RTL.
256 for (size_t i = 0; i < numGlyphs; ++i) {
257 bool runEnd = i + 1 == numGlyphs;
258 uint16_t glyph = glyphInfos[i].codepoint;
259 float offsetX = nextOffsetX;
260 float offsetY = nextOffsetY;
261 float advance = harfbuzzPositionToFloat(glyphPositions[i].x_advance);
262 nextOffsetX = runEnd ? 0 : harfbuzzPositionToFloat(glyphPositions[i + 1].x_offset);
263 nextOffsetY = runEnd ? 0 : -harfbuzzPositionToFloat(glyphPositions[i + 1].y_offset);
264
265 unsigned currentCharacterIndex = m_startIndexOfCurrentRun + glyphInfos[i].cluster;
266 bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster;
267 float spacing = isClusterEnd ? m_letterSpacing : 0;
268
269 if (isClusterEnd && isWordEnd(currentCharacterIndex))
270 spacing += determineWordBreakSpacing();
271
272 if (m_currentFontData->isZeroWidthSpaceGlyph(glyph)) {
273 currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0);
274 if (glyphBuffer)
275 glyphBuffer->add(glyph, m_currentFontData, createGlyphBufferAdvance(0, 0));
276 continue;
277 }
278
279 advance += spacing;
280 currentRun->setGlyphAndPositions(i, glyph, totalAdvance + offsetX, offsetY, advance);
281 if (glyphBuffer) {
282 float glyphAdvanceX = advance + nextOffsetX - offsetX;
283 float glyphAdvanceY = nextOffsetY - offsetY;
284 glyphBuffer->add(glyph, m_currentFontData, createGlyphBufferAdvance(glyphAdvanceX, glyphAdvanceY));
285 }
286
287 totalAdvance += advance;
288 }
289 currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0);
290 m_totalWidth += currentRun->width();
291}
292
293int HarfBuzzShaper::offsetForPosition(float targetX)
294{
295 int charactersSoFar = 0;
296 int currentX = 0;
297
298 if (m_run.rtl()) {
299 charactersSoFar = m_normalizedBufferLength;
300 for (int i = m_harfbuzzRuns.size() - 1; i >= 0; --i) {
301 charactersSoFar -= m_harfbuzzRuns[i]->numCharacters();
302 int nextX = currentX + m_harfbuzzRuns[i]->width();
303 if (currentX <= targetX && targetX <= nextX) {
304 // The x value in question is within this script run.
305 const unsigned index = m_harfbuzzRuns[i]->characterIndexForXPosition(targetX - currentX);
306 return charactersSoFar + index;
307 }
308 currentX = nextX;
309 }
310 } else {
311 for (unsigned i = 0; i < m_harfbuzzRuns.size(); ++i) {
312 int nextX = currentX + m_harfbuzzRuns[i]->width();
313 if (currentX <= targetX && targetX <= nextX) {
314 const unsigned index = m_harfbuzzRuns[i]->characterIndexForXPosition(targetX - currentX);
315 return charactersSoFar + index;
316 }
317 charactersSoFar += m_harfbuzzRuns[i]->numCharacters();
318 currentX = nextX;
319 }
320 }
321
322 return charactersSoFar;
323}
324
325FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to)
326{
327 int fromX = -1, toX = -1;
328 int currentX = 0;
329 // Iterate through the script runs in logical order, searching for the run covering the positions of interest.
330 for (unsigned i = 0; i < m_harfbuzzRuns.size(); ++i) {
331 int numCharacters = m_harfbuzzRuns[i]->numCharacters();
332 if (fromX == -1 && from >= 0 && from < numCharacters)
333 fromX = m_harfbuzzRuns[i]->xPositionForOffset(from) + currentX;
334 else
335 from -= numCharacters;
336
337 if (toX == -1 && to >= 0 && to < numCharacters)
338 toX = m_harfbuzzRuns[i]->xPositionForOffset(to) + currentX;
339 else
340 to -= numCharacters;
341
342 if (fromX != -1 && toX != -1)
343 break;
344 currentX += m_harfbuzzRuns[i]->width();
345 }
346
347 // The position in question might be just after the text.
348 if (fromX == -1)
349 fromX = 0;
350 if (toX == -1)
351 toX = m_run.rtl() ? 0 : m_totalWidth;
352
353 ASSERT(fromX != -1 && toX != -1);
354
355 if (fromX < toX)
356 return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
357 return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
358}
359
360} // namespace WebCore