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 "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
32 
33 // clang-format off
34 #include <hb.h>
35 #include <hb-ot.h>
36 // clang-format on
37 
38 #include <memory>
39 
40 #include "build/build_config.h"
41 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
42 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
43 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
44 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h"
45 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_data.h"
46 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
47 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
48 #include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
49 #include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
50 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
51 #include "third_party/blink/renderer/platform/resolution_units.h"
52 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
53 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
54 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
55 #include "third_party/harfbuzz-ng/utils/hb_scoped.h"
56 #include "third_party/skia/include/core/SkPaint.h"
57 #include "third_party/skia/include/core/SkPath.h"
58 #include "third_party/skia/include/core/SkPoint.h"
59 #include "third_party/skia/include/core/SkRect.h"
60 #include "third_party/skia/include/core/SkStream.h"
61 #include "third_party/skia/include/core/SkTypeface.h"
62 
63 namespace blink {
64 
65 namespace {
66 
67 #if defined(OS_MACOSX)
DetermineTrakSbix(SkTypeface * typeface,bool * has_trak,bool * has_sbix)68 void DetermineTrakSbix(SkTypeface* typeface, bool* has_trak, bool* has_sbix) {
69   int num_tags = typeface->countTables();
70 
71   SkFontTableTag tags[num_tags];
72 
73   int returned_tags = typeface->getTableTags(tags);
74   DCHECK_EQ(num_tags, returned_tags);
75 
76   for (auto& tag : tags) {
77     if (tag == SkSetFourByteTag('t', 'r', 'a', 'k'))
78       *has_trak = true;
79     if (tag == SkSetFourByteTag('s', 'b', 'i', 'x'))
80       *has_sbix = true;
81   }
82 }
83 #endif
84 
85 }  // namespace
86 
87 static scoped_refptr<HbFontCacheEntry> CreateHbFontCacheEntry(
88     hb_face_t*,
89     SkTypeface* typefaces);
90 
HarfBuzzFace(FontPlatformData * platform_data,uint64_t unique_id)91 HarfBuzzFace::HarfBuzzFace(FontPlatformData* platform_data, uint64_t unique_id)
92     : platform_data_(platform_data), unique_id_(unique_id) {
93   HarfBuzzFontCache::AddResult result =
94       FontGlobalContext::GetHarfBuzzFontCache()->insert(unique_id_, nullptr);
95   if (result.is_new_entry) {
96     HbScoped<hb_face_t> face(CreateFace());
97     result.stored_value->value =
98         CreateHbFontCacheEntry(face.get(), platform_data->Typeface());
99   }
100   result.stored_value->value->AddRef();
101   unscaled_font_ = result.stored_value->value->HbFont();
102   harfbuzz_font_data_ = result.stored_value->value->HbFontData();
103 }
104 
~HarfBuzzFace()105 HarfBuzzFace::~HarfBuzzFace() {
106   HarfBuzzFontCache* harfbuzz_font_cache =
107       FontGlobalContext::GetHarfBuzzFontCache();
108   HarfBuzzFontCache::iterator result = harfbuzz_font_cache->find(unique_id_);
109   SECURITY_DCHECK(result != harfbuzz_font_cache->end());
110   DCHECK(!result.Get()->value->HasOneRef());
111   result.Get()->value->Release();
112   if (result.Get()->value->HasOneRef())
113     harfbuzz_font_cache->erase(unique_id_);
114 }
115 
HarfBuzzGetGlyph(hb_font_t * hb_font,void * font_data,hb_codepoint_t unicode,hb_codepoint_t variation_selector,hb_codepoint_t * glyph,void * user_data)116 static hb_bool_t HarfBuzzGetGlyph(hb_font_t* hb_font,
117                                   void* font_data,
118                                   hb_codepoint_t unicode,
119                                   hb_codepoint_t variation_selector,
120                                   hb_codepoint_t* glyph,
121                                   void* user_data) {
122   HarfBuzzFontData* hb_font_data =
123       reinterpret_cast<HarfBuzzFontData*>(font_data);
124 
125   CHECK(hb_font_data);
126   if (hb_font_data->range_set_ && !hb_font_data->range_set_->Contains(unicode))
127     return false;
128 
129   return hb_font_get_glyph(hb_font_get_parent(hb_font), unicode,
130                            variation_selector, glyph);
131 }
132 
HarfBuzzGetNominalGlyph(hb_font_t * hb_font,void * font_data,hb_codepoint_t unicode,hb_codepoint_t * glyph,void * user_data)133 static hb_bool_t HarfBuzzGetNominalGlyph(hb_font_t* hb_font,
134                                          void* font_data,
135                                          hb_codepoint_t unicode,
136                                          hb_codepoint_t* glyph,
137                                          void* user_data) {
138   return HarfBuzzGetGlyph(hb_font, font_data, unicode, 0, glyph, user_data);
139 }
140 
HarfBuzzGetGlyphHorizontalAdvance(hb_font_t * hb_font,void * font_data,hb_codepoint_t glyph,void * user_data)141 static hb_position_t HarfBuzzGetGlyphHorizontalAdvance(hb_font_t* hb_font,
142                                                        void* font_data,
143                                                        hb_codepoint_t glyph,
144                                                        void* user_data) {
145   HarfBuzzFontData* hb_font_data =
146       reinterpret_cast<HarfBuzzFontData*>(font_data);
147   hb_position_t advance = 0;
148 
149   SkFontGetGlyphWidthForHarfBuzz(hb_font_data->font_, glyph, &advance);
150   return advance;
151 }
152 
HarfBuzzGetGlyphHorizontalAdvances(hb_font_t * font,void * font_data,unsigned count,const hb_codepoint_t * first_glyph,unsigned int glyph_stride,hb_position_t * first_advance,unsigned int advance_stride,void * user_data)153 static void HarfBuzzGetGlyphHorizontalAdvances(
154     hb_font_t* font,
155     void* font_data,
156     unsigned count,
157     const hb_codepoint_t* first_glyph,
158     unsigned int glyph_stride,
159     hb_position_t* first_advance,
160     unsigned int advance_stride,
161     void* user_data) {
162   HarfBuzzFontData* hb_font_data =
163       reinterpret_cast<HarfBuzzFontData*>(font_data);
164   SkFontGetGlyphWidthForHarfBuzz(hb_font_data->font_, count, first_glyph,
165                                  glyph_stride, first_advance, advance_stride);
166 }
167 
HarfBuzzGetGlyphVerticalOrigin(hb_font_t * hb_font,void * font_data,hb_codepoint_t glyph,hb_position_t * x,hb_position_t * y,void * user_data)168 static hb_bool_t HarfBuzzGetGlyphVerticalOrigin(hb_font_t* hb_font,
169                                                 void* font_data,
170                                                 hb_codepoint_t glyph,
171                                                 hb_position_t* x,
172                                                 hb_position_t* y,
173                                                 void* user_data) {
174   HarfBuzzFontData* hb_font_data =
175       reinterpret_cast<HarfBuzzFontData*>(font_data);
176   scoped_refptr<OpenTypeVerticalData> vertical_data =
177       hb_font_data->VerticalData();
178   if (!vertical_data)
179     return false;
180 
181   float result[] = {0, 0};
182   Glyph the_glyph = glyph;
183   vertical_data->GetVerticalTranslationsForGlyphs(hb_font_data->font_,
184                                                   &the_glyph, 1, result);
185   *x = SkiaScalarToHarfBuzzPosition(-result[0]);
186   *y = SkiaScalarToHarfBuzzPosition(-result[1]);
187   return true;
188 }
189 
HarfBuzzGetGlyphVerticalAdvance(hb_font_t * hb_font,void * font_data,hb_codepoint_t glyph,void * user_data)190 static hb_position_t HarfBuzzGetGlyphVerticalAdvance(hb_font_t* hb_font,
191                                                      void* font_data,
192                                                      hb_codepoint_t glyph,
193                                                      void* user_data) {
194   HarfBuzzFontData* hb_font_data =
195       reinterpret_cast<HarfBuzzFontData*>(font_data);
196   scoped_refptr<OpenTypeVerticalData> vertical_data =
197       hb_font_data->VerticalData();
198   if (!vertical_data) {
199     return SkiaScalarToHarfBuzzPosition(hb_font_data->height_fallback_);
200   }
201 
202   Glyph the_glyph = glyph;
203   float advance_height = -vertical_data->AdvanceHeight(the_glyph);
204   return SkiaScalarToHarfBuzzPosition(SkFloatToScalar(advance_height));
205 }
206 
HarfBuzzGetGlyphExtents(hb_font_t * hb_font,void * font_data,hb_codepoint_t glyph,hb_glyph_extents_t * extents,void * user_data)207 static hb_bool_t HarfBuzzGetGlyphExtents(hb_font_t* hb_font,
208                                          void* font_data,
209                                          hb_codepoint_t glyph,
210                                          hb_glyph_extents_t* extents,
211                                          void* user_data) {
212   HarfBuzzFontData* hb_font_data =
213       reinterpret_cast<HarfBuzzFontData*>(font_data);
214 
215   SkFontGetGlyphExtentsForHarfBuzz(hb_font_data->font_, glyph, extents);
216   return true;
217 }
218 
TableHasSpace(hb_face_t * face,hb_set_t * glyphs,hb_tag_t tag,hb_codepoint_t space)219 static inline bool TableHasSpace(hb_face_t* face,
220                                  hb_set_t* glyphs,
221                                  hb_tag_t tag,
222                                  hb_codepoint_t space) {
223   unsigned count = hb_ot_layout_table_get_lookup_count(face, tag);
224   for (unsigned i = 0; i < count; i++) {
225     hb_ot_layout_lookup_collect_glyphs(face, tag, i, glyphs, glyphs, glyphs,
226                                        nullptr);
227     if (hb_set_has(glyphs, space))
228       return true;
229   }
230   return false;
231 }
232 
GetSpaceGlyph(hb_font_t * font,hb_codepoint_t & space)233 static bool GetSpaceGlyph(hb_font_t* font, hb_codepoint_t& space) {
234   return hb_font_get_nominal_glyph(font, kSpaceCharacter, &space);
235 }
236 
HasSpaceInLigaturesOrKerning(TypesettingFeatures features)237 bool HarfBuzzFace::HasSpaceInLigaturesOrKerning(TypesettingFeatures features) {
238   const hb_codepoint_t kInvalidCodepoint = static_cast<hb_codepoint_t>(-1);
239   hb_codepoint_t space = kInvalidCodepoint;
240 
241   HbScoped<hb_set_t> glyphs(hb_set_create());
242 
243   // Check whether computing is needed and compute for gpos/gsub.
244   if (features & kKerning &&
245       harfbuzz_font_data_->space_in_gpos_ ==
246           HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Unknown) {
247     if (space == kInvalidCodepoint && !GetSpaceGlyph(unscaled_font_, space))
248       return false;
249     // Compute for gpos.
250     hb_face_t* face = hb_font_get_face(unscaled_font_);
251     DCHECK(face);
252     harfbuzz_font_data_->space_in_gpos_ =
253         hb_ot_layout_has_positioning(face) &&
254                 TableHasSpace(face, glyphs.get(), HB_OT_TAG_GPOS, space)
255             ? HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present
256             : HarfBuzzFontData::SpaceGlyphInOpenTypeTables::NotPresent;
257   }
258 
259   hb_set_clear(glyphs.get());
260 
261   if (features & kLigatures &&
262       harfbuzz_font_data_->space_in_gsub_ ==
263           HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Unknown) {
264     if (space == kInvalidCodepoint && !GetSpaceGlyph(unscaled_font_, space))
265       return false;
266     // Compute for gpos.
267     hb_face_t* face = hb_font_get_face(unscaled_font_);
268     DCHECK(face);
269     harfbuzz_font_data_->space_in_gsub_ =
270         hb_ot_layout_has_substitution(face) &&
271                 TableHasSpace(face, glyphs.get(), HB_OT_TAG_GSUB, space)
272             ? HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present
273             : HarfBuzzFontData::SpaceGlyphInOpenTypeTables::NotPresent;
274   }
275 
276   return (features & kKerning &&
277           harfbuzz_font_data_->space_in_gpos_ ==
278               HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present) ||
279          (features & kLigatures &&
280           harfbuzz_font_data_->space_in_gsub_ ==
281               HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present);
282 }
283 
UnitsPerEmFromHeadTable()284 unsigned HarfBuzzFace::UnitsPerEmFromHeadTable() {
285   hb_face_t* face = hb_font_get_face(unscaled_font_);
286   return hb_face_get_upem(face);
287 }
288 
ShouldSubpixelPosition()289 bool HarfBuzzFace::ShouldSubpixelPosition() {
290   return harfbuzz_font_data_->font_.isSubpixel();
291 }
292 
create_populated_hb_font_funcs(FontGlobalContext::HorizontalAdvanceSource horizontal_advance_source)293 static hb_font_funcs_t* create_populated_hb_font_funcs(
294     FontGlobalContext::HorizontalAdvanceSource horizontal_advance_source) {
295   hb_font_funcs_t* funcs = hb_font_funcs_create();
296 
297   if (horizontal_advance_source == FontGlobalContext::kSkiaHorizontalAdvances) {
298     hb_font_funcs_set_glyph_h_advance_func(
299         funcs, HarfBuzzGetGlyphHorizontalAdvance, nullptr, nullptr);
300     hb_font_funcs_set_glyph_h_advances_func(
301         funcs, (hb_font_get_glyph_h_advances_func_t)HarfBuzzGetGlyphHorizontalAdvances, nullptr, nullptr);
302   }
303   hb_font_funcs_set_variation_glyph_func(funcs, HarfBuzzGetGlyph, nullptr,
304                                          nullptr);
305   hb_font_funcs_set_nominal_glyph_func(funcs, HarfBuzzGetNominalGlyph, nullptr,
306                                        nullptr);
307   // TODO(https://crbug.com/899718): Replace vertical metrics callbacks with
308   // HarfBuzz VORG/VMTX internal implementation by deregistering those.
309   hb_font_funcs_set_glyph_v_advance_func(funcs, HarfBuzzGetGlyphVerticalAdvance,
310                                          nullptr, nullptr);
311   hb_font_funcs_set_glyph_v_origin_func(funcs, HarfBuzzGetGlyphVerticalOrigin,
312                                         nullptr, nullptr);
313   hb_font_funcs_set_glyph_extents_func(funcs, HarfBuzzGetGlyphExtents, nullptr,
314                                        nullptr);
315 
316   hb_font_funcs_make_immutable(funcs);
317   return funcs;
318 }
319 
HarfBuzzSkiaGetFontFuncs(FontGlobalContext::HorizontalAdvanceSource advance_source)320 static hb_font_funcs_t* HarfBuzzSkiaGetFontFuncs(
321     FontGlobalContext::HorizontalAdvanceSource advance_source) {
322   hb_font_funcs_t* funcs =
323       FontGlobalContext::GetHarfBuzzFontFuncs(advance_source);
324 
325   // We don't set callback functions which we can't support.
326   // HarfBuzz will use the fallback implementation if they aren't set.
327   if (!funcs) {
328     funcs = create_populated_hb_font_funcs(advance_source);
329     FontGlobalContext::SetHarfBuzzFontFuncs(advance_source, funcs);
330   }
331   DCHECK(funcs);
332   return funcs;
333 }
334 
HarfBuzzSkiaGetTable(hb_face_t * face,hb_tag_t tag,void * user_data)335 static hb_blob_t* HarfBuzzSkiaGetTable(hb_face_t* face,
336                                        hb_tag_t tag,
337                                        void* user_data) {
338   SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data);
339 
340   const wtf_size_t table_size =
341       SafeCast<wtf_size_t>(typeface->getTableSize(tag));
342   if (!table_size) {
343     return nullptr;
344   }
345 
346   char* buffer = reinterpret_cast<char*>(WTF::Partitions::FastMalloc(
347       table_size, WTF_HEAP_PROFILER_TYPE_NAME(HarfBuzzFontData)));
348   if (!buffer)
349     return nullptr;
350   size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer);
351   if (table_size != actual_size) {
352     WTF::Partitions::FastFree(buffer);
353     return nullptr;
354   }
355   return hb_blob_create(const_cast<char*>(buffer), table_size,
356                         HB_MEMORY_MODE_WRITABLE, buffer,
357                         WTF::Partitions::FastFree);
358 }
359 
360 #if !defined(OS_MACOSX)
DeleteTypefaceStream(void * stream_asset_ptr)361 static void DeleteTypefaceStream(void* stream_asset_ptr) {
362   SkStreamAsset* stream_asset =
363       reinterpret_cast<SkStreamAsset*>(stream_asset_ptr);
364   delete stream_asset;
365 }
366 #endif
367 
CreateFace()368 hb_face_t* HarfBuzzFace::CreateFace() {
369   hb_face_t* face = nullptr;
370 
371   DEFINE_THREAD_SAFE_STATIC_LOCAL(BooleanHistogram, zero_copy_success_histogram,
372                                   ("Blink.Fonts.HarfBuzzFaceZeroCopyAccess"));
373   SkTypeface* typeface = platform_data_->Typeface();
374   CHECK(typeface);
375   // The attempt of doing zero copy-mmaped memory access to the font blobs does
376   // not work efficiently on Mac, since what is returned from
377   // typeface->openStream is a synthesized font assembled from copying all font
378   // tables on Mac. See the implementation of SkTypeface_Mac::onOpenStream.
379 #if !defined(OS_MACOSX)
380   int ttc_index = 0;
381   std::unique_ptr<SkStreamAsset> tf_stream(typeface->openStream(&ttc_index));
382   if (tf_stream && tf_stream->getMemoryBase()) {
383     const void* tf_memory = tf_stream->getMemoryBase();
384     size_t tf_size = tf_stream->getLength();
385     HbScoped<hb_blob_t> face_blob(
386         hb_blob_create(reinterpret_cast<const char*>(tf_memory),
387                        SafeCast<unsigned int>(tf_size), HB_MEMORY_MODE_READONLY,
388                        tf_stream.release(), DeleteTypefaceStream));
389     face = hb_face_create(face_blob.get(), ttc_index);
390   }
391 #endif
392 
393   // Fallback to table copies if there is no in-memory access.
394   if (!face) {
395     face = hb_face_create_for_tables(HarfBuzzSkiaGetTable,
396                                      platform_data_->Typeface(), nullptr);
397     zero_copy_success_histogram.Count(false);
398   } else {
399     zero_copy_success_histogram.Count(true);
400   }
401 
402   DCHECK(face);
403   return face;
404 }
405 
CreateHbFontCacheEntry(hb_face_t * face,SkTypeface * typeface)406 scoped_refptr<HbFontCacheEntry> CreateHbFontCacheEntry(hb_face_t* face,
407                                                        SkTypeface* typeface) {
408   HbScoped<hb_font_t> ot_font(hb_font_create(face));
409   hb_ot_font_set_funcs(ot_font.get());
410 
411   int axis_count = typeface->getVariationDesignPosition(nullptr, 0);
412   if (axis_count > 0) {
413     Vector<SkFontArguments::VariationPosition::Coordinate> axis_values;
414     axis_values.resize(axis_count);
415     if (typeface->getVariationDesignPosition(axis_values.data(),
416                                              axis_values.size()) > 0) {
417       hb_font_set_variations(
418           ot_font.get(), reinterpret_cast<hb_variation_t*>(axis_values.data()),
419           axis_values.size());
420     }
421   }
422 
423   // Creating a sub font means that non-available functions
424   // are found from the parent.
425   hb_font_t* unscaled_font = hb_font_create_sub_font(ot_font.get());
426   scoped_refptr<HbFontCacheEntry> cache_entry =
427       HbFontCacheEntry::Create(unscaled_font);
428 
429   FontGlobalContext::HorizontalAdvanceSource advance_source =
430       FontGlobalContext::kSkiaHorizontalAdvances;
431 #if defined(OS_MACOSX)
432   bool has_trak = false;
433   bool has_sbix = false;
434   DetermineTrakSbix(typeface, &has_trak, &has_sbix);
435   if (has_trak && !has_sbix)
436     advance_source = FontGlobalContext::kHarfBuzzHorizontalAdvances;
437 #endif
438   hb_font_set_funcs(unscaled_font, HarfBuzzSkiaGetFontFuncs(advance_source),
439                     cache_entry->HbFontData(), nullptr);
440   return cache_entry;
441 }
442 
443 static_assert(
444     std::is_same<decltype(SkFontArguments::VariationPosition::Coordinate::axis),
445                  decltype(hb_variation_t::tag)>::value &&
446         std::is_same<
447             decltype(SkFontArguments::VariationPosition::Coordinate::value),
448             decltype(hb_variation_t::value)>::value &&
449         sizeof(SkFontArguments::VariationPosition::Coordinate) ==
450             sizeof(hb_variation_t),
451     "Skia and HarfBuzz Variation parameter types must match in structure and "
452     "size.");
453 
GetScaledFont(scoped_refptr<UnicodeRangeSet> range_set,VerticalLayoutCallbacks vertical_layout) const454 hb_font_t* HarfBuzzFace::GetScaledFont(
455     scoped_refptr<UnicodeRangeSet> range_set,
456     VerticalLayoutCallbacks vertical_layout) const {
457   harfbuzz_font_data_->range_set_ = std::move(range_set);
458   harfbuzz_font_data_->UpdateFallbackMetricsAndScale(*platform_data_,
459                                                      vertical_layout);
460 
461   int scale = SkiaScalarToHarfBuzzPosition(platform_data_->size());
462   hb_font_set_scale(unscaled_font_, scale, scale);
463   // See contended discussion in https://github.com/harfbuzz/harfbuzz/pull/1484
464   // Setting ptem here is critical for HarfBuzz to know where to lookup spacing
465   // offset in the AAT trak table, the unit pt in ptem here means "CoreText"
466   // points. After discussion on the pull request and with Apple developers, the
467   // meaning of HarfBuzz' hb_font_set_ptem API was changed to expect the
468   // equivalent of CSS pixels here.
469   hb_font_set_ptem(unscaled_font_, platform_data_->size());
470 
471   return unscaled_font_;
472 }
473 
474 }  // namespace blink
475