Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp

Issue 2515493002: Refactor HarfBuzzShaper to not retain font data (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2012 Google Inc. All rights reserved. 2 * Copyright (c) 2012 Google Inc. All rights reserved.
3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are 6 * modification, are permitted provided that the following conditions are
7 * met: 7 * met:
8 * 8 *
9 * * Redistributions of source code must retain the above copyright 9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
(...skipping 17 matching lines...) Expand all
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */ 30 */
31 31
32 #include "platform/fonts/shaping/HarfBuzzShaper.h" 32 #include "platform/fonts/shaping/HarfBuzzShaper.h"
33 33
34 #include "platform/RuntimeEnabledFeatures.h" 34 #include "platform/RuntimeEnabledFeatures.h"
35 #include "platform/fonts/Font.h" 35 #include "platform/fonts/Font.h"
36 #include "platform/fonts/FontFallbackIterator.h" 36 #include "platform/fonts/FontFallbackIterator.h"
37 #include "platform/fonts/GlyphBuffer.h" 37 #include "platform/fonts/GlyphBuffer.h"
38 #include "platform/fonts/SmallCapsIterator.h"
38 #include "platform/fonts/UTF16TextIterator.h" 39 #include "platform/fonts/UTF16TextIterator.h"
39 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" 40 #include "platform/fonts/opentype/OpenTypeCapsSupport.h"
40 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" 41 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h"
41 #include "platform/fonts/shaping/HarfBuzzFace.h" 42 #include "platform/fonts/shaping/HarfBuzzFace.h"
42 #include "platform/fonts/shaping/RunSegmenter.h" 43 #include "platform/fonts/shaping/RunSegmenter.h"
43 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" 44 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h"
44 #include "platform/text/Character.h" 45 #include "platform/text/Character.h"
45 #include "platform/text/TextBreakIterator.h" 46 #include "platform/text/TextBreakIterator.h"
46 #include "wtf/Compiler.h" 47 #include "wtf/Compiler.h"
47 #include "wtf/MathExtras.h" 48 #include "wtf/MathExtras.h"
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 character = zeroWidthSpaceCharacter; 115 character = zeroWidthSpaceCharacter;
115 } else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) { 116 } else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) {
116 character = zeroWidthSpaceCharacter; 117 character = zeroWidthSpaceCharacter;
117 } 118 }
118 119
119 U16_APPEND(destination, *destinationLength, length, character, error); 120 U16_APPEND(destination, *destinationLength, length, character, error);
120 DCHECK(!error); 121 DCHECK(!error);
121 } 122 }
122 } 123 }
123 124
124 HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run) 125 HarfBuzzShaper::HarfBuzzShaper(const TextRun& run)
125 : m_font(font), m_textRun(run), m_normalizedBufferLength(0) { 126 : m_textRun(run), m_normalizedBufferLength(0) {
126 m_normalizedBuffer = wrapArrayUnique(new UChar[m_textRun.length() + 1]); 127 m_normalizedBuffer = wrapArrayUnique(new UChar[m_textRun.length() + 1]);
127 normalizeCharacters(m_textRun, m_textRun.length(), m_normalizedBuffer.get(), 128 normalizeCharacters(m_textRun, m_textRun.length(), m_normalizedBuffer.get(),
128 &m_normalizedBufferLength); 129 &m_normalizedBufferLength);
129 setFontFeatures();
130 } 130 }
131 131
132 static inline hb_feature_t createFeature(uint8_t c1, 132 namespace {
133 uint8_t c2,
134 uint8_t c3,
135 uint8_t c4,
136 uint32_t value = 0) {
137 return {HB_TAG(c1, c2, c3, c4), value, 0 /* start */,
138 static_cast<unsigned>(-1) /* end */};
139 }
140
141 void HarfBuzzShaper::setFontFeatures() {
142 const FontDescription& description = m_font->getFontDescription();
143
144 static hb_feature_t noKern = createFeature('k', 'e', 'r', 'n');
145 static hb_feature_t noVkrn = createFeature('v', 'k', 'r', 'n');
146 switch (description.getKerning()) {
147 case FontDescription::NormalKerning:
148 // kern/vkrn are enabled by default
149 break;
150 case FontDescription::NoneKerning:
151 m_features.append(description.isVerticalAnyUpright() ? noVkrn : noKern);
152 break;
153 case FontDescription::AutoKerning:
154 break;
155 }
156
157 static hb_feature_t noClig = createFeature('c', 'l', 'i', 'g');
158 static hb_feature_t noLiga = createFeature('l', 'i', 'g', 'a');
159 switch (description.commonLigaturesState()) {
160 case FontDescription::DisabledLigaturesState:
161 m_features.append(noLiga);
162 m_features.append(noClig);
163 break;
164 case FontDescription::EnabledLigaturesState:
165 // liga and clig are on by default
166 break;
167 case FontDescription::NormalLigaturesState:
168 break;
169 }
170 static hb_feature_t dlig = createFeature('d', 'l', 'i', 'g', 1);
171 switch (description.discretionaryLigaturesState()) {
172 case FontDescription::DisabledLigaturesState:
173 // dlig is off by default
174 break;
175 case FontDescription::EnabledLigaturesState:
176 m_features.append(dlig);
177 break;
178 case FontDescription::NormalLigaturesState:
179 break;
180 }
181 static hb_feature_t hlig = createFeature('h', 'l', 'i', 'g', 1);
182 switch (description.historicalLigaturesState()) {
183 case FontDescription::DisabledLigaturesState:
184 // hlig is off by default
185 break;
186 case FontDescription::EnabledLigaturesState:
187 m_features.append(hlig);
188 break;
189 case FontDescription::NormalLigaturesState:
190 break;
191 }
192 static hb_feature_t noCalt = createFeature('c', 'a', 'l', 't');
193 switch (description.contextualLigaturesState()) {
194 case FontDescription::DisabledLigaturesState:
195 m_features.append(noCalt);
196 break;
197 case FontDescription::EnabledLigaturesState:
198 // calt is on by default
199 break;
200 case FontDescription::NormalLigaturesState:
201 break;
202 }
203
204 static hb_feature_t hwid = createFeature('h', 'w', 'i', 'd', 1);
205 static hb_feature_t twid = createFeature('t', 'w', 'i', 'd', 1);
206 static hb_feature_t qwid = createFeature('q', 'w', 'i', 'd', 1);
207 switch (description.widthVariant()) {
208 case HalfWidth:
209 m_features.append(hwid);
210 break;
211 case ThirdWidth:
212 m_features.append(twid);
213 break;
214 case QuarterWidth:
215 m_features.append(qwid);
216 break;
217 case RegularWidth:
218 break;
219 }
220
221 // font-variant-numeric:
222 static hb_feature_t lnum = createFeature('l', 'n', 'u', 'm', 1);
223 if (description.variantNumeric().numericFigureValue() ==
224 FontVariantNumeric::LiningNums)
225 m_features.append(lnum);
226
227 static hb_feature_t onum = createFeature('o', 'n', 'u', 'm', 1);
228 if (description.variantNumeric().numericFigureValue() ==
229 FontVariantNumeric::OldstyleNums)
230 m_features.append(onum);
231
232 static hb_feature_t pnum = createFeature('p', 'n', 'u', 'm', 1);
233 if (description.variantNumeric().numericSpacingValue() ==
234 FontVariantNumeric::ProportionalNums)
235 m_features.append(pnum);
236 static hb_feature_t tnum = createFeature('t', 'n', 'u', 'm', 1);
237 if (description.variantNumeric().numericSpacingValue() ==
238 FontVariantNumeric::TabularNums)
239 m_features.append(tnum);
240
241 static hb_feature_t afrc = createFeature('a', 'f', 'r', 'c', 1);
242 if (description.variantNumeric().numericFractionValue() ==
243 FontVariantNumeric::StackedFractions)
244 m_features.append(afrc);
245 static hb_feature_t frac = createFeature('f', 'r', 'a', 'c', 1);
246 if (description.variantNumeric().numericFractionValue() ==
247 FontVariantNumeric::DiagonalFractions)
248 m_features.append(frac);
249
250 static hb_feature_t ordn = createFeature('o', 'r', 'd', 'n', 1);
251 if (description.variantNumeric().ordinalValue() ==
252 FontVariantNumeric::OrdinalOn)
253 m_features.append(ordn);
254
255 static hb_feature_t zero = createFeature('z', 'e', 'r', 'o', 1);
256 if (description.variantNumeric().slashedZeroValue() ==
257 FontVariantNumeric::SlashedZeroOn)
258 m_features.append(zero);
259
260 FontFeatureSettings* settings = description.featureSettings();
261 if (!settings)
262 return;
263
264 // TODO(drott): crbug.com/450619 Implement feature resolution instead of
265 // just appending the font-feature-settings.
266 unsigned numFeatures = settings->size();
267 for (unsigned i = 0; i < numFeatures; ++i) {
268 hb_feature_t feature;
269 const AtomicString& tag = settings->at(i).tag();
270 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);
271 feature.value = settings->at(i).value();
272 feature.start = 0;
273 feature.end = static_cast<unsigned>(-1);
274 m_features.append(feature);
275 }
276 }
277
278 HarfBuzzShaper::CapsFeatureSettingsScopedOverlay::
279 CapsFeatureSettingsScopedOverlay(
280 FeaturesVector& features,
281 FontDescription::FontVariantCaps variantCaps)
282 : m_features(features), m_countFeatures(0) {
283 overlayCapsFeatures(variantCaps);
284 }
285
286 void HarfBuzzShaper::CapsFeatureSettingsScopedOverlay::overlayCapsFeatures(
287 FontDescription::FontVariantCaps variantCaps) {
288 static hb_feature_t smcp = createFeature('s', 'm', 'c', 'p', 1);
289 static hb_feature_t pcap = createFeature('p', 'c', 'a', 'p', 1);
290 static hb_feature_t c2sc = createFeature('c', '2', 's', 'c', 1);
291 static hb_feature_t c2pc = createFeature('c', '2', 'p', 'c', 1);
292 static hb_feature_t unic = createFeature('u', 'n', 'i', 'c', 1);
293 static hb_feature_t titl = createFeature('t', 'i', 't', 'l', 1);
294 if (variantCaps == FontDescription::SmallCaps ||
295 variantCaps == FontDescription::AllSmallCaps) {
296 prependCounting(smcp);
297 if (variantCaps == FontDescription::AllSmallCaps) {
298 prependCounting(c2sc);
299 }
300 }
301 if (variantCaps == FontDescription::PetiteCaps ||
302 variantCaps == FontDescription::AllPetiteCaps) {
303 prependCounting(pcap);
304 if (variantCaps == FontDescription::AllPetiteCaps) {
305 prependCounting(c2pc);
306 }
307 }
308 if (variantCaps == FontDescription::Unicase) {
309 prependCounting(unic);
310 }
311 if (variantCaps == FontDescription::TitlingCaps) {
312 prependCounting(titl);
313 }
314 }
315
316 void HarfBuzzShaper::CapsFeatureSettingsScopedOverlay::prependCounting(
317 const hb_feature_t& feature) {
318 m_features.prepend(feature);
319 m_countFeatures++;
320 }
321
322 HarfBuzzShaper::CapsFeatureSettingsScopedOverlay::
323 ~CapsFeatureSettingsScopedOverlay() {
324 m_features.remove(0, m_countFeatures);
325 }
326 133
327 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built 134 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built
328 // without hb-icu. See http://crbug.com/356929 135 // without hb-icu. See http://crbug.com/356929
329 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) { 136 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) {
330 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) 137 if (UNLIKELY(script == USCRIPT_INVALID_CODE))
331 return HB_SCRIPT_INVALID; 138 return HB_SCRIPT_INVALID;
332 139
333 return hb_script_from_string(uscript_getShortName(script), -1); 140 return hb_script_from_string(uscript_getShortName(script), -1);
334 } 141 }
335 142
336 static inline hb_direction_t TextDirectionToHBDirection( 143 static inline hb_direction_t TextDirectionToHBDirection(
337 TextDirection dir, 144 TextDirection dir,
338 FontOrientation orientation, 145 FontOrientation orientation,
339 const SimpleFontData* fontData) { 146 const SimpleFontData* fontData) {
340 hb_direction_t harfBuzzDirection = 147 hb_direction_t harfBuzzDirection =
341 isVerticalAnyUpright(orientation) && 148 isVerticalAnyUpright(orientation) &&
342 !fontData->isTextOrientationFallback() 149 !fontData->isTextOrientationFallback()
343 ? HB_DIRECTION_TTB 150 ? HB_DIRECTION_TTB
344 : HB_DIRECTION_LTR; 151 : HB_DIRECTION_LTR;
345 return dir == RTL ? HB_DIRECTION_REVERSE(harfBuzzDirection) 152 return dir == RTL ? HB_DIRECTION_REVERSE(harfBuzzDirection)
346 : harfBuzzDirection; 153 : harfBuzzDirection;
347 } 154 }
348 155
156 } // namespace
157
349 inline bool HarfBuzzShaper::shapeRange( 158 inline bool HarfBuzzShaper::shapeRange(
350 hb_buffer_t* harfBuzzBuffer, 159 hb_buffer_t* harfBuzzBuffer,
160 const Font* font,
161 const FeaturesVector& fontFeatures,
351 const SimpleFontData* currentFont, 162 const SimpleFontData* currentFont,
352 PassRefPtr<UnicodeRangeSet> currentFontRangeSet, 163 PassRefPtr<UnicodeRangeSet> currentFontRangeSet,
353 UScriptCode currentRunScript, 164 UScriptCode currentRunScript,
354 hb_language_t language) { 165 hb_language_t language) {
355 const FontPlatformData* platformData = &(currentFont->platformData()); 166 const FontPlatformData* platformData = &(currentFont->platformData());
356 HarfBuzzFace* face = platformData->harfBuzzFace(); 167 HarfBuzzFace* face = platformData->harfBuzzFace();
357 if (!face) { 168 if (!face) {
358 DLOG(ERROR) << "Could not create HarfBuzzFace from FontPlatformData."; 169 DLOG(ERROR) << "Could not create HarfBuzzFace from FontPlatformData.";
359 return false; 170 return false;
360 } 171 }
361 172
362 hb_buffer_set_language(harfBuzzBuffer, language); 173 hb_buffer_set_language(harfBuzzBuffer, language);
363 hb_buffer_set_script(harfBuzzBuffer, ICUScriptToHBScript(currentRunScript)); 174 hb_buffer_set_script(harfBuzzBuffer, ICUScriptToHBScript(currentRunScript));
364 hb_buffer_set_direction( 175 hb_buffer_set_direction(
365 harfBuzzBuffer, 176 harfBuzzBuffer,
366 TextDirectionToHBDirection(m_textRun.direction(), 177 TextDirectionToHBDirection(m_textRun.direction(),
367 m_font->getFontDescription().orientation(), 178 font->getFontDescription().orientation(),
368 currentFont)); 179 currentFont));
369 180
370 hb_font_t* hbFont = face->getScaledFont(std::move(currentFontRangeSet)); 181 hb_font_t* hbFont = face->getScaledFont(std::move(currentFontRangeSet));
371 hb_shape(hbFont, harfBuzzBuffer, m_features.isEmpty() ? 0 : m_features.data(), 182 hb_shape(hbFont, harfBuzzBuffer,
372 m_features.size()); 183 fontFeatures.isEmpty() ? 0 : fontFeatures.data(),
184 fontFeatures.size());
373 185
374 return true; 186 return true;
375 } 187 }
376 188
377 bool HarfBuzzShaper::extractShapeResults(hb_buffer_t* harfBuzzBuffer, 189 bool HarfBuzzShaper::extractShapeResults(hb_buffer_t* harfBuzzBuffer,
378 ShapeResult* shapeResult, 190 ShapeResult* shapeResult,
379 bool& fontCycleQueued, 191 bool& fontCycleQueued,
192 Deque<HolesQueueItem>* holesQueue,
380 const HolesQueueItem& currentQueueItem, 193 const HolesQueueItem& currentQueueItem,
194 const Font* font,
381 const SimpleFontData* currentFont, 195 const SimpleFontData* currentFont,
382 UScriptCode currentRunScript, 196 UScriptCode currentRunScript,
383 bool isLastResort) { 197 bool isLastResort) {
384 enum ClusterResult { Shaped, NotDef, Unknown }; 198 enum ClusterResult { Shaped, NotDef, Unknown };
385 ClusterResult currentClusterResult = Unknown; 199 ClusterResult currentClusterResult = Unknown;
386 ClusterResult previousClusterResult = Unknown; 200 ClusterResult previousClusterResult = Unknown;
387 unsigned previousCluster = 0; 201 unsigned previousCluster = 0;
388 unsigned currentCluster = 0; 202 unsigned currentCluster = 0;
389 203
390 // Find first notdef glyph in harfBuzzBuffer. 204 // Find first notdef glyph in harfBuzzBuffer.
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
467 } else { 281 } else {
468 numCharacters = glyphInfo[lastChangePosition - 1].cluster - 282 numCharacters = glyphInfo[lastChangePosition - 1].cluster -
469 glyphInfo[glyphIndex - 1].cluster; 283 glyphInfo[glyphIndex - 1].cluster;
470 } 284 }
471 numGlyphsToInsert = glyphIndex - lastChangePosition; 285 numGlyphsToInsert = glyphIndex - lastChangePosition;
472 } 286 }
473 287
474 if (currentClusterResult == Shaped && !isLastResort) { 288 if (currentClusterResult == Shaped && !isLastResort) {
475 // Now it's clear that we need to continue processing. 289 // Now it's clear that we need to continue processing.
476 if (!fontCycleQueued) { 290 if (!fontCycleQueued) {
477 appendToHolesQueue(HolesQueueNextFont, 0, 0); 291 holesQueue->append(HolesQueueItem(HolesQueueNextFont, 0, 0));
478 fontCycleQueued = true; 292 fontCycleQueued = true;
479 } 293 }
480 294
481 // Here we need to put character positions. 295 // Here we need to put character positions.
482 ASSERT(numCharacters); 296 ASSERT(numCharacters);
483 appendToHolesQueue(HolesQueueRange, startIndex, numCharacters); 297 holesQueue->append(
298 HolesQueueItem(HolesQueueRange, startIndex, numCharacters));
484 } 299 }
485 300
486 // If numCharacters is 0, that means we hit a NotDef before shaping the 301 // If numCharacters is 0, that means we hit a NotDef before shaping the
487 // whole grapheme. We do not append it here. For the next glyph we 302 // whole grapheme. We do not append it here. For the next glyph we
488 // encounter, atChange will be true, and the characters corresponding to 303 // encounter, atChange will be true, and the characters corresponding to
489 // the grapheme will be added to the TODO queue again, attempting to 304 // the grapheme will be added to the TODO queue again, attempting to
490 // shape the whole grapheme with the next font. 305 // shape the whole grapheme with the next font.
491 // When we're getting here with the last resort font, we have no other 306 // When we're getting here with the last resort font, we have no other
492 // choice than adding boxes to the ShapeResult. 307 // choice than adding boxes to the ShapeResult.
493 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) { 308 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) {
494 hb_direction_t direction = TextDirectionToHBDirection( 309 hb_direction_t direction = TextDirectionToHBDirection(
495 m_textRun.direction(), m_font->getFontDescription().orientation(), 310 m_textRun.direction(), font->getFontDescription().orientation(),
496 currentFont); 311 currentFont);
497 // Here we need to specify glyph positions. 312 // Here we need to specify glyph positions.
498 ShapeResult::RunInfo* run = new ShapeResult::RunInfo( 313 ShapeResult::RunInfo* run = new ShapeResult::RunInfo(
499 currentFont, direction, ICUScriptToHBScript(currentRunScript), 314 currentFont, direction, ICUScriptToHBScript(currentRunScript),
500 startIndex, numGlyphsToInsert, numCharacters); 315 startIndex, numGlyphsToInsert, numCharacters);
501 shapeResult->insertRun(wrapUnique(run), lastChangePosition, 316 shapeResult->insertRun(wrapUnique(run), lastChangePosition,
502 numGlyphsToInsert, harfBuzzBuffer); 317 numGlyphsToInsert, harfBuzzBuffer);
503 } 318 }
504 lastChangePosition = glyphIndex; 319 lastChangePosition = glyphIndex;
505 } 320 }
506 return true; 321 return true;
507 } 322 }
508 323
509 static inline const SimpleFontData* fontDataAdjustedForOrientation( 324 static inline const SimpleFontData* fontDataAdjustedForOrientation(
510 const SimpleFontData* originalFont, 325 const SimpleFontData* originalFont,
511 FontOrientation runOrientation, 326 FontOrientation runOrientation,
512 OrientationIterator::RenderOrientation renderOrientation) { 327 OrientationIterator::RenderOrientation renderOrientation) {
513 if (!isVerticalBaseline(runOrientation)) 328 if (!isVerticalBaseline(runOrientation))
514 return originalFont; 329 return originalFont;
515 330
516 if (runOrientation == FontOrientation::VerticalRotated || 331 if (runOrientation == FontOrientation::VerticalRotated ||
517 (runOrientation == FontOrientation::VerticalMixed && 332 (runOrientation == FontOrientation::VerticalMixed &&
518 renderOrientation == OrientationIterator::OrientationRotateSideways)) 333 renderOrientation == OrientationIterator::OrientationRotateSideways))
519 return originalFont->verticalRightOrientationFontData().get(); 334 return originalFont->verticalRightOrientationFontData().get();
520 335
521 return originalFont; 336 return originalFont;
522 } 337 }
523 338
524 bool HarfBuzzShaper::collectFallbackHintChars(Vector<UChar32>& hint) { 339 bool HarfBuzzShaper::collectFallbackHintChars(
525 if (!m_holesQueue.size()) 340 const Deque<HolesQueueItem>& holesQueue,
341 Vector<UChar32>& hint) {
342 if (!holesQueue.size())
526 return false; 343 return false;
527 344
528 hint.clear(); 345 hint.clear();
529 346
530 size_t numCharsAdded = 0; 347 size_t numCharsAdded = 0;
531 for (auto it = m_holesQueue.begin(); it != m_holesQueue.end(); ++it) { 348 for (auto it = holesQueue.begin(); it != holesQueue.end(); ++it) {
532 if (it->m_action == HolesQueueNextFont) 349 if (it->m_action == HolesQueueNextFont)
533 break; 350 break;
534 351
535 UChar32 hintChar; 352 UChar32 hintChar;
536 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <= 353 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <=
537 m_normalizedBufferLength); 354 m_normalizedBufferLength);
538 UTF16TextIterator iterator(m_normalizedBuffer.get() + it->m_startIndex, 355 UTF16TextIterator iterator(m_normalizedBuffer.get() + it->m_startIndex,
539 it->m_numCharacters); 356 it->m_numCharacters);
540 while (iterator.consume(hintChar)) { 357 while (iterator.consume(hintChar)) {
541 hint.append(hintChar); 358 hint.append(hintChar);
542 numCharsAdded++; 359 numCharsAdded++;
543 iterator.advance(); 360 iterator.advance();
544 } 361 }
545 } 362 }
546 return numCharsAdded > 0; 363 return numCharsAdded > 0;
547 } 364 }
548 365
549 void HarfBuzzShaper::appendToHolesQueue(HolesQueueItemAction action, 366 namespace {
550 unsigned startIndex, 367 using HolesQueueItem = HarfBuzzShaper::HolesQueueItem;
551 unsigned numCharacters) { 368 using HolesQueueItemAction = HarfBuzzShaper::HolesQueueItemAction;
552 m_holesQueue.append(HolesQueueItem(action, startIndex, numCharacters)); 369
553 } 370 void splitUntilNextCaseChange(
554 371 const UChar* normalizedBuffer,
555 void HarfBuzzShaper::prependHolesQueue(HolesQueueItemAction action, 372 Deque<HolesQueueItem>* queue,
556 unsigned startIndex,
557 unsigned numCharacters) {
558 m_holesQueue.prepend(HolesQueueItem(action, startIndex, numCharacters));
559 }
560
561 void HarfBuzzShaper::splitUntilNextCaseChange(
562 HolesQueueItem& currentQueueItem, 373 HolesQueueItem& currentQueueItem,
563 SmallCapsIterator::SmallCapsBehavior& smallCapsBehavior) { 374 SmallCapsIterator::SmallCapsBehavior& smallCapsBehavior) {
564 unsigned numCharactersUntilCaseChange = 0; 375 unsigned numCharactersUntilCaseChange = 0;
565 SmallCapsIterator smallCapsIterator( 376 SmallCapsIterator smallCapsIterator(
566 m_normalizedBuffer.get() + currentQueueItem.m_startIndex, 377 normalizedBuffer + currentQueueItem.m_startIndex,
567 currentQueueItem.m_numCharacters); 378 currentQueueItem.m_numCharacters);
568 smallCapsIterator.consume(&numCharactersUntilCaseChange, &smallCapsBehavior); 379 smallCapsIterator.consume(&numCharactersUntilCaseChange, &smallCapsBehavior);
569 if (numCharactersUntilCaseChange > 0 && 380 if (numCharactersUntilCaseChange > 0 &&
570 numCharactersUntilCaseChange < currentQueueItem.m_numCharacters) { 381 numCharactersUntilCaseChange < currentQueueItem.m_numCharacters) {
571 prependHolesQueue( 382 unsigned startIndex =
572 HolesQueueRange, 383 currentQueueItem.m_startIndex + numCharactersUntilCaseChange;
573 currentQueueItem.m_startIndex + numCharactersUntilCaseChange, 384 unsigned numCharacters =
574 currentQueueItem.m_numCharacters - numCharactersUntilCaseChange); 385 currentQueueItem.m_numCharacters - numCharactersUntilCaseChange;
386 queue->prepend(HolesQueueItem(HolesQueueItemAction::HolesQueueRange,
387 startIndex, numCharacters));
575 currentQueueItem.m_numCharacters = numCharactersUntilCaseChange; 388 currentQueueItem.m_numCharacters = numCharactersUntilCaseChange;
576 } 389 }
577 } 390 }
578 391
579 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult() { 392 hb_feature_t createFeature(uint8_t c1,
393 uint8_t c2,
394 uint8_t c3,
395 uint8_t c4,
396 uint32_t value = 0) {
397 return {HB_TAG(c1, c2, c3, c4), value, 0 /* start */,
398 static_cast<unsigned>(-1) /* end */};
399 }
400
401 void setFontFeatures(const Font* font,
402 HarfBuzzShaper::FeaturesVector* features) {
403 const FontDescription& description = font->getFontDescription();
404
405 static hb_feature_t noKern = createFeature('k', 'e', 'r', 'n');
406 static hb_feature_t noVkrn = createFeature('v', 'k', 'r', 'n');
407 switch (description.getKerning()) {
408 case FontDescription::NormalKerning:
409 // kern/vkrn are enabled by default
410 break;
411 case FontDescription::NoneKerning:
412 features->append(description.isVerticalAnyUpright() ? noVkrn : noKern);
413 break;
414 case FontDescription::AutoKerning:
415 break;
416 }
417
418 static hb_feature_t noClig = createFeature('c', 'l', 'i', 'g');
419 static hb_feature_t noLiga = createFeature('l', 'i', 'g', 'a');
420 switch (description.commonLigaturesState()) {
421 case FontDescription::DisabledLigaturesState:
422 features->append(noLiga);
423 features->append(noClig);
424 break;
425 case FontDescription::EnabledLigaturesState:
426 // liga and clig are on by default
427 break;
428 case FontDescription::NormalLigaturesState:
429 break;
430 }
431 static hb_feature_t dlig = createFeature('d', 'l', 'i', 'g', 1);
432 switch (description.discretionaryLigaturesState()) {
433 case FontDescription::DisabledLigaturesState:
434 // dlig is off by default
435 break;
436 case FontDescription::EnabledLigaturesState:
437 features->append(dlig);
438 break;
439 case FontDescription::NormalLigaturesState:
440 break;
441 }
442 static hb_feature_t hlig = createFeature('h', 'l', 'i', 'g', 1);
443 switch (description.historicalLigaturesState()) {
444 case FontDescription::DisabledLigaturesState:
445 // hlig is off by default
446 break;
447 case FontDescription::EnabledLigaturesState:
448 features->append(hlig);
449 break;
450 case FontDescription::NormalLigaturesState:
451 break;
452 }
453 static hb_feature_t noCalt = createFeature('c', 'a', 'l', 't');
454 switch (description.contextualLigaturesState()) {
455 case FontDescription::DisabledLigaturesState:
456 features->append(noCalt);
457 break;
458 case FontDescription::EnabledLigaturesState:
459 // calt is on by default
460 break;
461 case FontDescription::NormalLigaturesState:
462 break;
463 }
464
465 static hb_feature_t hwid = createFeature('h', 'w', 'i', 'd', 1);
466 static hb_feature_t twid = createFeature('t', 'w', 'i', 'd', 1);
467 static hb_feature_t qwid = createFeature('q', 'w', 'i', 'd', 1);
468 switch (description.widthVariant()) {
469 case HalfWidth:
470 features->append(hwid);
471 break;
472 case ThirdWidth:
473 features->append(twid);
474 break;
475 case QuarterWidth:
476 features->append(qwid);
477 break;
478 case RegularWidth:
479 break;
480 }
481
482 // font-variant-numeric:
483 static hb_feature_t lnum = createFeature('l', 'n', 'u', 'm', 1);
484 if (description.variantNumeric().numericFigureValue() ==
485 FontVariantNumeric::LiningNums)
486 features->append(lnum);
487
488 static hb_feature_t onum = createFeature('o', 'n', 'u', 'm', 1);
489 if (description.variantNumeric().numericFigureValue() ==
490 FontVariantNumeric::OldstyleNums)
491 features->append(onum);
492
493 static hb_feature_t pnum = createFeature('p', 'n', 'u', 'm', 1);
494 if (description.variantNumeric().numericSpacingValue() ==
495 FontVariantNumeric::ProportionalNums)
496 features->append(pnum);
497 static hb_feature_t tnum = createFeature('t', 'n', 'u', 'm', 1);
498 if (description.variantNumeric().numericSpacingValue() ==
499 FontVariantNumeric::TabularNums)
500 features->append(tnum);
501
502 static hb_feature_t afrc = createFeature('a', 'f', 'r', 'c', 1);
503 if (description.variantNumeric().numericFractionValue() ==
504 FontVariantNumeric::StackedFractions)
505 features->append(afrc);
506 static hb_feature_t frac = createFeature('f', 'r', 'a', 'c', 1);
507 if (description.variantNumeric().numericFractionValue() ==
508 FontVariantNumeric::DiagonalFractions)
509 features->append(frac);
510
511 static hb_feature_t ordn = createFeature('o', 'r', 'd', 'n', 1);
512 if (description.variantNumeric().ordinalValue() ==
513 FontVariantNumeric::OrdinalOn)
514 features->append(ordn);
515
516 static hb_feature_t zero = createFeature('z', 'e', 'r', 'o', 1);
517 if (description.variantNumeric().slashedZeroValue() ==
518 FontVariantNumeric::SlashedZeroOn)
519 features->append(zero);
520
521 FontFeatureSettings* settings = description.featureSettings();
522 if (!settings)
523 return;
524
525 // TODO(drott): crbug.com/450619 Implement feature resolution instead of
526 // just appending the font-feature-settings.
527 unsigned numFeatures = settings->size();
528 for (unsigned i = 0; i < numFeatures; ++i) {
529 hb_feature_t feature;
530 const AtomicString& tag = settings->at(i).tag();
531 feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);
532 feature.value = settings->at(i).value();
533 feature.start = 0;
534 feature.end = static_cast<unsigned>(-1);
535 features->append(feature);
536 }
537 }
538
539 class CapsFeatureSettingsScopedOverlay final {
540 STACK_ALLOCATED()
541
542 public:
543 CapsFeatureSettingsScopedOverlay(HarfBuzzShaper::FeaturesVector*,
544 FontDescription::FontVariantCaps);
545 CapsFeatureSettingsScopedOverlay() = delete;
546 ~CapsFeatureSettingsScopedOverlay();
547
548 private:
549 void overlayCapsFeatures(FontDescription::FontVariantCaps);
550 void prependCounting(const hb_feature_t&);
551 HarfBuzzShaper::FeaturesVector* m_features;
552 size_t m_countFeatures;
553 };
554
555 CapsFeatureSettingsScopedOverlay::CapsFeatureSettingsScopedOverlay(
556 HarfBuzzShaper::FeaturesVector* features,
557 FontDescription::FontVariantCaps variantCaps)
558 : m_features(features), m_countFeatures(0) {
559 overlayCapsFeatures(variantCaps);
560 }
561
562 void CapsFeatureSettingsScopedOverlay::overlayCapsFeatures(
563 FontDescription::FontVariantCaps variantCaps) {
564 static hb_feature_t smcp = createFeature('s', 'm', 'c', 'p', 1);
565 static hb_feature_t pcap = createFeature('p', 'c', 'a', 'p', 1);
566 static hb_feature_t c2sc = createFeature('c', '2', 's', 'c', 1);
567 static hb_feature_t c2pc = createFeature('c', '2', 'p', 'c', 1);
568 static hb_feature_t unic = createFeature('u', 'n', 'i', 'c', 1);
569 static hb_feature_t titl = createFeature('t', 'i', 't', 'l', 1);
570 if (variantCaps == FontDescription::SmallCaps ||
571 variantCaps == FontDescription::AllSmallCaps) {
572 prependCounting(smcp);
573 if (variantCaps == FontDescription::AllSmallCaps) {
574 prependCounting(c2sc);
575 }
576 }
577 if (variantCaps == FontDescription::PetiteCaps ||
578 variantCaps == FontDescription::AllPetiteCaps) {
579 prependCounting(pcap);
580 if (variantCaps == FontDescription::AllPetiteCaps) {
581 prependCounting(c2pc);
582 }
583 }
584 if (variantCaps == FontDescription::Unicase) {
585 prependCounting(unic);
586 }
587 if (variantCaps == FontDescription::TitlingCaps) {
588 prependCounting(titl);
589 }
590 }
591
592 void CapsFeatureSettingsScopedOverlay::prependCounting(
593 const hb_feature_t& feature) {
594 m_features->prepend(feature);
595 m_countFeatures++;
596 }
597
598 CapsFeatureSettingsScopedOverlay::~CapsFeatureSettingsScopedOverlay() {
599 m_features->remove(0, m_countFeatures);
600 }
601
602 } // namespace
603
604 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult(const Font* font) {
580 RefPtr<ShapeResult> result = ShapeResult::create( 605 RefPtr<ShapeResult> result = ShapeResult::create(
581 m_font, m_normalizedBufferLength, m_textRun.direction()); 606 font, m_normalizedBufferLength, m_textRun.direction());
582 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), 607 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(),
583 hb_buffer_destroy); 608 hb_buffer_destroy);
584 609 FeaturesVector fontFeatures;
585 const FontDescription& fontDescription = m_font->getFontDescription(); 610 setFontFeatures(font, &fontFeatures);
611 const FontDescription& fontDescription = font->getFontDescription();
586 const hb_language_t language = 612 const hb_language_t language =
587 fontDescription.localeOrDefault().harfbuzzLanguage(); 613 fontDescription.localeOrDefault().harfbuzzLanguage();
588 614
589 bool needsCapsHandling = 615 bool needsCapsHandling =
590 fontDescription.variantCaps() != FontDescription::CapsNormal; 616 fontDescription.variantCaps() != FontDescription::CapsNormal;
591 OpenTypeCapsSupport capsSupport; 617 OpenTypeCapsSupport capsSupport;
592 618
593 RunSegmenter::RunSegmenterRange segmentRange = { 619 RunSegmenter::RunSegmenterRange segmentRange = {
594 0, 0, USCRIPT_INVALID_CODE, OrientationIterator::OrientationInvalid, 620 0, 0, USCRIPT_INVALID_CODE, OrientationIterator::OrientationInvalid,
595 FontFallbackPriority::Invalid}; 621 FontFallbackPriority::Invalid};
596 RunSegmenter runSegmenter(m_normalizedBuffer.get(), m_normalizedBufferLength, 622 RunSegmenter runSegmenter(m_normalizedBuffer.get(), m_normalizedBufferLength,
597 m_font->getFontDescription().orientation()); 623 font->getFontDescription().orientation());
598 624
599 Vector<UChar32> fallbackCharsHint; 625 Vector<UChar32> fallbackCharsHint;
600 626
601 // TODO: Check whether this treatAsZerowidthspace from the previous script 627 // TODO: Check whether this treatAsZerowidthspace from the previous script
602 // segmentation plays a role here, does the new scriptRuniterator handle that 628 // segmentation plays a role here, does the new scriptRuniterator handle that
603 // correctly? 629 // correctly?
630 Deque<HolesQueueItem> holesQueue;
604 while (runSegmenter.consume(&segmentRange)) { 631 while (runSegmenter.consume(&segmentRange)) {
605 RefPtr<FontFallbackIterator> fallbackIterator = 632 RefPtr<FontFallbackIterator> fallbackIterator =
606 m_font->createFontFallbackIterator(segmentRange.fontFallbackPriority); 633 font->createFontFallbackIterator(segmentRange.fontFallbackPriority);
607 634
608 appendToHolesQueue(HolesQueueNextFont, 0, 0); 635 holesQueue.append(HolesQueueItem(HolesQueueNextFont, 0, 0));
609 appendToHolesQueue(HolesQueueRange, segmentRange.start, 636 holesQueue.append(HolesQueueItem(HolesQueueRange, segmentRange.start,
610 segmentRange.end - segmentRange.start); 637 segmentRange.end - segmentRange.start));
611 638
612 RefPtr<FontDataForRangeSet> currentFontDataForRangeSet; 639 RefPtr<FontDataForRangeSet> currentFontDataForRangeSet;
613 640
614 bool fontCycleQueued = false; 641 bool fontCycleQueued = false;
615 while (m_holesQueue.size()) { 642 while (holesQueue.size()) {
616 HolesQueueItem currentQueueItem = m_holesQueue.takeFirst(); 643 HolesQueueItem currentQueueItem = holesQueue.takeFirst();
617 644
618 if (currentQueueItem.m_action == HolesQueueNextFont) { 645 if (currentQueueItem.m_action == HolesQueueNextFont) {
619 // For now, we're building a character list with which we probe 646 // For now, we're building a character list with which we probe
620 // for needed fonts depending on the declared unicode-range of a 647 // for needed fonts depending on the declared unicode-range of a
621 // segmented CSS font. Alternatively, we can build a fake font 648 // segmented CSS font. Alternatively, we can build a fake font
622 // for the shaper and check whether any glyphs were found, or 649 // for the shaper and check whether any glyphs were found, or
623 // define a new API on the shaper which will give us coverage 650 // define a new API on the shaper which will give us coverage
624 // information? 651 // information?
625 if (!collectFallbackHintChars(fallbackCharsHint)) { 652 if (!collectFallbackHintChars(holesQueue, fallbackCharsHint)) {
626 // Give up shaping since we cannot retrieve a font fallback 653 // Give up shaping since we cannot retrieve a font fallback
627 // font without a hintlist. 654 // font without a hintlist.
628 m_holesQueue.clear(); 655 holesQueue.clear();
629 break; 656 break;
630 } 657 }
631 658
632 currentFontDataForRangeSet = fallbackIterator->next(fallbackCharsHint); 659 currentFontDataForRangeSet = fallbackIterator->next(fallbackCharsHint);
633 660
634 if (!currentFontDataForRangeSet->fontData()) { 661 if (!currentFontDataForRangeSet->fontData()) {
635 ASSERT(!m_holesQueue.size()); 662 DCHECK(!holesQueue.size());
636 break; 663 break;
637 } 664 }
638 fontCycleQueued = false; 665 fontCycleQueued = false;
639 continue; 666 continue;
640 } 667 }
641 668
642 SmallCapsIterator::SmallCapsBehavior smallCapsBehavior = 669 SmallCapsIterator::SmallCapsBehavior smallCapsBehavior =
643 SmallCapsIterator::SmallCapsSameCase; 670 SmallCapsIterator::SmallCapsSameCase;
644 if (needsCapsHandling) { 671 if (needsCapsHandling) {
645 capsSupport = 672 capsSupport =
646 OpenTypeCapsSupport(currentFontDataForRangeSet->fontData() 673 OpenTypeCapsSupport(currentFontDataForRangeSet->fontData()
647 ->platformData() 674 ->platformData()
648 .harfBuzzFace(), 675 .harfBuzzFace(),
649 fontDescription.variantCaps(), 676 fontDescription.variantCaps(),
650 ICUScriptToHBScript(segmentRange.script)); 677 ICUScriptToHBScript(segmentRange.script));
651 if (capsSupport.needsRunCaseSplitting()) 678 if (capsSupport.needsRunCaseSplitting()) {
652 splitUntilNextCaseChange(currentQueueItem, smallCapsBehavior); 679 splitUntilNextCaseChange(m_normalizedBuffer.get(), &holesQueue,
680 currentQueueItem, smallCapsBehavior);
681 }
653 } 682 }
654 683
655 ASSERT(currentQueueItem.m_numCharacters); 684 ASSERT(currentQueueItem.m_numCharacters);
656 685
657 const SimpleFontData* smallcapsAdjustedFont = 686 const SimpleFontData* smallcapsAdjustedFont =
658 needsCapsHandling && capsSupport.needsSyntheticFont(smallCapsBehavior) 687 needsCapsHandling && capsSupport.needsSyntheticFont(smallCapsBehavior)
659 ? currentFontDataForRangeSet->fontData() 688 ? currentFontDataForRangeSet->fontData()
660 ->smallCapsFontData(fontDescription) 689 ->smallCapsFontData(fontDescription)
661 .get() 690 .get()
662 : currentFontDataForRangeSet->fontData(); 691 : currentFontDataForRangeSet->fontData();
663 692
664 // Compatibility with SimpleFontData approach of keeping a flag for 693 // Compatibility with SimpleFontData approach of keeping a flag for
665 // overriding drawing direction. 694 // overriding drawing direction.
666 // TODO: crbug.com/506224 This should go away in favor of storing that 695 // TODO: crbug.com/506224 This should go away in favor of storing that
667 // information elsewhere, for example in ShapeResult. 696 // information elsewhere, for example in ShapeResult.
668 const SimpleFontData* directionAndSmallCapsAdjustedFont = 697 const SimpleFontData* directionAndSmallCapsAdjustedFont =
669 fontDataAdjustedForOrientation( 698 fontDataAdjustedForOrientation(
670 smallcapsAdjustedFont, m_font->getFontDescription().orientation(), 699 smallcapsAdjustedFont, font->getFontDescription().orientation(),
671 segmentRange.renderOrientation); 700 segmentRange.renderOrientation);
672 701
673 CaseMapIntend caseMapIntend = CaseMapIntend::KeepSameCase; 702 CaseMapIntend caseMapIntend = CaseMapIntend::KeepSameCase;
674 if (needsCapsHandling) { 703 if (needsCapsHandling) {
675 caseMapIntend = capsSupport.needsCaseChange(smallCapsBehavior); 704 caseMapIntend = capsSupport.needsCaseChange(smallCapsBehavior);
676 } 705 }
677 706
678 CaseMappingHarfBuzzBufferFiller( 707 CaseMappingHarfBuzzBufferFiller(
679 caseMapIntend, fontDescription.localeOrDefault(), 708 caseMapIntend, fontDescription.localeOrDefault(),
680 harfBuzzBuffer.get(), m_normalizedBuffer.get(), 709 harfBuzzBuffer.get(), m_normalizedBuffer.get(),
681 m_normalizedBufferLength, currentQueueItem.m_startIndex, 710 m_normalizedBufferLength, currentQueueItem.m_startIndex,
682 currentQueueItem.m_numCharacters); 711 currentQueueItem.m_numCharacters);
683 712
684 CapsFeatureSettingsScopedOverlay capsOverlay( 713 CapsFeatureSettingsScopedOverlay capsOverlay(
685 m_features, capsSupport.fontFeatureToUse(smallCapsBehavior)); 714 &fontFeatures, capsSupport.fontFeatureToUse(smallCapsBehavior));
686 715
687 if (!shapeRange(harfBuzzBuffer.get(), directionAndSmallCapsAdjustedFont, 716 if (!shapeRange(harfBuzzBuffer.get(), font, fontFeatures,
717 directionAndSmallCapsAdjustedFont,
688 currentFontDataForRangeSet->ranges(), segmentRange.script, 718 currentFontDataForRangeSet->ranges(), segmentRange.script,
689 language)) 719 language))
690 DLOG(ERROR) << "Shaping range failed."; 720 DLOG(ERROR) << "Shaping range failed.";
691 721
692 if (!extractShapeResults( 722 if (!extractShapeResults(
693 harfBuzzBuffer.get(), result.get(), fontCycleQueued, 723 harfBuzzBuffer.get(), result.get(), fontCycleQueued, &holesQueue,
694 currentQueueItem, directionAndSmallCapsAdjustedFont, 724 currentQueueItem, font, directionAndSmallCapsAdjustedFont,
695 segmentRange.script, !fallbackIterator->hasNext())) 725 segmentRange.script, !fallbackIterator->hasNext()))
696 DLOG(ERROR) << "Shape result extraction failed."; 726 DLOG(ERROR) << "Shape result extraction failed.";
697 727
698 hb_buffer_reset(harfBuzzBuffer.get()); 728 hb_buffer_reset(harfBuzzBuffer.get());
699 } 729 }
700 } 730 }
701 return result.release(); 731 return result.release();
702 } 732 }
703 733
704 } // namespace blink 734 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698