1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2000 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved.
6  * Copyright (c) 2007, 2008, 2010 Google Inc. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24 
25 #include "third_party/blink/renderer/platform/fonts/font.h"
26 
27 #include "cc/paint/paint_canvas.h"
28 #include "cc/paint/paint_flags.h"
29 #include "third_party/blink/renderer/platform/fonts/character_range.h"
30 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
31 #include "third_party/blink/renderer/platform/fonts/font_fallback_list.h"
32 #include "third_party/blink/renderer/platform/fonts/ng_text_fragment_paint_info.h"
33 #include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
34 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h"
35 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h"
36 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
37 #include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
38 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
39 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
40 #include "third_party/blink/renderer/platform/text/bidi_resolver.h"
41 #include "third_party/blink/renderer/platform/text/character.h"
42 #include "third_party/blink/renderer/platform/text/text_run.h"
43 #include "third_party/blink/renderer/platform/text/text_run_iterator.h"
44 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
45 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
46 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
47 #include "third_party/skia/include/core/SkTextBlob.h"
48 
49 namespace blink {
50 
51 Font::Font() = default;
52 
Font(const FontDescription & fd)53 Font::Font(const FontDescription& fd) : font_description_(fd) {}
54 
Font(const FontDescription & font_description,FontSelector * font_selector)55 Font::Font(const FontDescription& font_description, FontSelector* font_selector)
56     : font_description_(font_description),
57       font_fallback_list_(
58           font_selector ? FontFallbackList::Create(font_selector) : nullptr) {}
59 
60 Font::Font(const Font& other) = default;
61 
operator =(const Font & other)62 Font& Font::operator=(const Font& other) {
63   font_description_ = other.font_description_;
64   font_fallback_list_ = other.font_fallback_list_;
65   return *this;
66 }
67 
operator ==(const Font & other) const68 bool Font::operator==(const Font& other) const {
69   FontSelector* first =
70       font_fallback_list_ ? font_fallback_list_->GetFontSelector() : nullptr;
71   FontSelector* second = other.font_fallback_list_
72                              ? other.font_fallback_list_->GetFontSelector()
73                              : nullptr;
74 
75   return first == second && font_description_ == other.font_description_ &&
76          (font_fallback_list_
77               ? font_fallback_list_->FontSelectorVersion()
78               : 0) == (other.font_fallback_list_
79                            ? other.font_fallback_list_->FontSelectorVersion()
80                            : 0) &&
81          (font_fallback_list_ ? font_fallback_list_->Generation() : 0) ==
82              (other.font_fallback_list_
83                   ? other.font_fallback_list_->Generation()
84                   : 0);
85 }
86 
87 namespace {
88 
DrawBlobs(cc::PaintCanvas * canvas,const cc::PaintFlags & flags,const ShapeResultBloberizer::BlobBuffer & blobs,const FloatPoint & point,cc::NodeId node_id=cc::kInvalidNodeId)89 void DrawBlobs(cc::PaintCanvas* canvas,
90                const cc::PaintFlags& flags,
91                const ShapeResultBloberizer::BlobBuffer& blobs,
92                const FloatPoint& point,
93                cc::NodeId node_id = cc::kInvalidNodeId) {
94   for (const auto& blob_info : blobs) {
95     DCHECK(blob_info.blob);
96     cc::PaintCanvasAutoRestore auto_restore(canvas, false);
97     if (blob_info.rotation == CanvasRotationInVertical::kRotateCanvasUpright) {
98       canvas->save();
99 
100       SkMatrix m;
101       m.setSinCos(-1, 0, point.X(), point.Y());
102       canvas->concat(m);
103     }
104     if (node_id != cc::kInvalidNodeId) {
105       canvas->drawTextBlob(blob_info.blob, point.X(), point.Y(), node_id,
106                            flags);
107     } else {
108       canvas->drawTextBlob(blob_info.blob, point.X(), point.Y(), flags);
109     }
110   }
111 }
112 
113 }  // anonymous ns
114 
DrawText(cc::PaintCanvas * canvas,const TextRunPaintInfo & run_info,const FloatPoint & point,float device_scale_factor,const cc::PaintFlags & flags) const115 void Font::DrawText(cc::PaintCanvas* canvas,
116                     const TextRunPaintInfo& run_info,
117                     const FloatPoint& point,
118                     float device_scale_factor,
119                     const cc::PaintFlags& flags) const {
120   DrawText(canvas, run_info, point, device_scale_factor, cc::kInvalidNodeId,
121            flags);
122 }
123 
DrawText(cc::PaintCanvas * canvas,const TextRunPaintInfo & run_info,const FloatPoint & point,float device_scale_factor,cc::NodeId node_id,const cc::PaintFlags & flags) const124 void Font::DrawText(cc::PaintCanvas* canvas,
125                     const TextRunPaintInfo& run_info,
126                     const FloatPoint& point,
127                     float device_scale_factor,
128                     cc::NodeId node_id,
129                     const cc::PaintFlags& flags) const {
130   // Don't draw anything while we are using custom fonts that are in the process
131   // of loading.
132   if (ShouldSkipDrawing())
133     return;
134 
135   ShapeResultBloberizer bloberizer(*this, device_scale_factor);
136   CachingWordShaper word_shaper(*this);
137   ShapeResultBuffer buffer;
138   word_shaper.FillResultBuffer(run_info, &buffer);
139   bloberizer.FillGlyphs(run_info, buffer);
140   DrawBlobs(canvas, flags, bloberizer.Blobs(), point, node_id);
141 }
142 
DrawText(cc::PaintCanvas * canvas,const NGTextFragmentPaintInfo & text_info,const FloatPoint & point,float device_scale_factor,cc::NodeId node_id,const cc::PaintFlags & flags) const143 void Font::DrawText(cc::PaintCanvas* canvas,
144                     const NGTextFragmentPaintInfo& text_info,
145                     const FloatPoint& point,
146                     float device_scale_factor,
147                     cc::NodeId node_id,
148                     const cc::PaintFlags& flags) const {
149   // Don't draw anything while we are using custom fonts that are in the process
150   // of loading.
151   if (ShouldSkipDrawing())
152     return;
153 
154   ShapeResultBloberizer bloberizer(*this, device_scale_factor);
155   bloberizer.FillGlyphs(text_info.text, text_info.from, text_info.to,
156                         text_info.shape_result);
157   DrawBlobs(canvas, flags, bloberizer.Blobs(), point, node_id);
158 }
159 
DrawBidiText(cc::PaintCanvas * canvas,const TextRunPaintInfo & run_info,const FloatPoint & point,CustomFontNotReadyAction custom_font_not_ready_action,float device_scale_factor,const cc::PaintFlags & flags) const160 bool Font::DrawBidiText(cc::PaintCanvas* canvas,
161                         const TextRunPaintInfo& run_info,
162                         const FloatPoint& point,
163                         CustomFontNotReadyAction custom_font_not_ready_action,
164                         float device_scale_factor,
165                         const cc::PaintFlags& flags) const {
166   // Don't draw anything while we are using custom fonts that are in the process
167   // of loading, except if the 'force' argument is set to true (in which case it
168   // will use a fallback font).
169   if (ShouldSkipDrawing() &&
170       custom_font_not_ready_action == kDoNotPaintIfFontNotReady)
171     return false;
172 
173   // sub-run painting is not supported for Bidi text.
174   const TextRun& run = run_info.run;
175   DCHECK_EQ(run_info.from, 0u);
176   DCHECK_EQ(run_info.to, run.length());
177   BidiResolver<TextRunIterator, BidiCharacterRun> bidi_resolver;
178   bidi_resolver.SetStatus(
179       BidiStatus(run.Direction(), run.DirectionalOverride()));
180   bidi_resolver.SetPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
181 
182   // FIXME: This ownership should be reversed. We should pass BidiRunList
183   // to BidiResolver in createBidiRunsForLine.
184   BidiRunList<BidiCharacterRun>& bidi_runs = bidi_resolver.Runs();
185   bidi_resolver.CreateBidiRunsForLine(TextRunIterator(&run, run.length()));
186   if (!bidi_runs.RunCount())
187     return true;
188 
189   FloatPoint curr_point = point;
190   BidiCharacterRun* bidi_run = bidi_runs.FirstRun();
191   CachingWordShaper word_shaper(*this);
192   while (bidi_run) {
193     TextRun subrun =
194         run.SubRun(bidi_run->Start(), bidi_run->Stop() - bidi_run->Start());
195     bool is_rtl = bidi_run->Level() % 2;
196     subrun.SetDirection(is_rtl ? TextDirection::kRtl : TextDirection::kLtr);
197     subrun.SetDirectionalOverride(bidi_run->DirOverride(false));
198 
199     TextRunPaintInfo subrun_info(subrun);
200 
201     // Fix regression with -ftrivial-auto-var-init=pattern. See
202     // crbug.com/1055652.
203     STACK_UNINITIALIZED ShapeResultBloberizer bloberizer(*this,
204                                                          device_scale_factor);
205     ShapeResultBuffer buffer;
206     word_shaper.FillResultBuffer(subrun_info, &buffer);
207     float run_width = bloberizer.FillGlyphs(subrun_info, buffer);
208     DrawBlobs(canvas, flags, bloberizer.Blobs(), curr_point);
209 
210     bidi_run = bidi_run->Next();
211     curr_point.Move(run_width, 0);
212   }
213 
214   bidi_runs.DeleteRuns();
215   return true;
216 }
217 
DrawEmphasisMarks(cc::PaintCanvas * canvas,const TextRunPaintInfo & run_info,const AtomicString & mark,const FloatPoint & point,float device_scale_factor,const cc::PaintFlags & flags) const218 void Font::DrawEmphasisMarks(cc::PaintCanvas* canvas,
219                              const TextRunPaintInfo& run_info,
220                              const AtomicString& mark,
221                              const FloatPoint& point,
222                              float device_scale_factor,
223                              const cc::PaintFlags& flags) const {
224   if (ShouldSkipDrawing())
225     return;
226 
227   FontCachePurgePreventer purge_preventer;
228 
229   const auto emphasis_glyph_data = GetEmphasisMarkGlyphData(mark);
230   if (!emphasis_glyph_data.font_data)
231     return;
232 
233   ShapeResultBloberizer bloberizer(*this, device_scale_factor);
234   CachingWordShaper word_shaper(*this);
235   ShapeResultBuffer buffer;
236   word_shaper.FillResultBuffer(run_info, &buffer);
237   bloberizer.FillTextEmphasisGlyphs(run_info, emphasis_glyph_data, buffer);
238   DrawBlobs(canvas, flags, bloberizer.Blobs(), point);
239 }
240 
DrawEmphasisMarks(cc::PaintCanvas * canvas,const NGTextFragmentPaintInfo & text_info,const AtomicString & mark,const FloatPoint & point,float device_scale_factor,const cc::PaintFlags & flags) const241 void Font::DrawEmphasisMarks(cc::PaintCanvas* canvas,
242                              const NGTextFragmentPaintInfo& text_info,
243                              const AtomicString& mark,
244                              const FloatPoint& point,
245                              float device_scale_factor,
246                              const cc::PaintFlags& flags) const {
247   if (ShouldSkipDrawing())
248     return;
249 
250   FontCachePurgePreventer purge_preventer;
251   const auto emphasis_glyph_data = GetEmphasisMarkGlyphData(mark);
252   if (!emphasis_glyph_data.font_data)
253     return;
254 
255   ShapeResultBloberizer bloberizer(*this, device_scale_factor);
256   bloberizer.FillTextEmphasisGlyphs(text_info.text, text_info.from,
257                                     text_info.to, emphasis_glyph_data,
258                                     text_info.shape_result);
259   DrawBlobs(canvas, flags, bloberizer.Blobs(), point);
260 }
261 
TextInkBounds(const NGTextFragmentPaintInfo & text_info) const262 FloatRect Font::TextInkBounds(const NGTextFragmentPaintInfo& text_info) const {
263   // No need to compute bounds if using custom fonts that are in the process
264   // of loading as it won't be painted.
265   if (ShouldSkipDrawing())
266     return FloatRect();
267 
268   // NOTE(eae): We could use the SkTextBlob::bounds API [1] however by default
269   // it returns conservative bounds (rather than tight bounds) which are
270   // unsuitable for our needs. If we could get the tight bounds from Skia that
271   // would be quite a bit faster than the two-stage approach employed by the
272   // ShapeResultView::ComputeInkBounds method.
273   // 1: https://skia.org/user/api/SkTextBlob_Reference#SkTextBlob_bounds
274   return text_info.shape_result->ComputeInkBounds();
275 }
276 
Width(const TextRun & run,HashSet<const SimpleFontData * > * fallback_fonts,FloatRect * glyph_bounds) const277 float Font::Width(const TextRun& run,
278                   HashSet<const SimpleFontData*>* fallback_fonts,
279                   FloatRect* glyph_bounds) const {
280   FontCachePurgePreventer purge_preventer;
281   CachingWordShaper shaper(*this);
282   return shaper.Width(run, fallback_fonts, glyph_bounds);
283 }
284 
285 namespace {  // anonymous namespace
286 
InterceptsFromBlobs(const ShapeResultBloberizer::BlobBuffer & blobs,const SkPaint & paint,const std::tuple<float,float> & bounds,SkScalar * intercepts_buffer)287 unsigned InterceptsFromBlobs(const ShapeResultBloberizer::BlobBuffer& blobs,
288                              const SkPaint& paint,
289                              const std::tuple<float, float>& bounds,
290                              SkScalar* intercepts_buffer) {
291   SkScalar bounds_array[2] = {std::get<0>(bounds), std::get<1>(bounds)};
292 
293   unsigned num_intervals = 0;
294   for (const auto& blob_info : blobs) {
295     DCHECK(blob_info.blob);
296 
297     // ShapeResultBloberizer splits for a new blob rotation, but does not split
298     // for a change in font. A TextBlob can contain runs with differing fonts
299     // and the getTextBlobIntercepts method handles multiple fonts for us. For
300     // upright in vertical blobs we currently have to bail, see crbug.com/655154
301     if (blob_info.rotation == CanvasRotationInVertical::kRotateCanvasUpright)
302       continue;
303 
304     SkScalar* offset_intercepts_buffer = nullptr;
305     if (intercepts_buffer)
306       offset_intercepts_buffer = &intercepts_buffer[num_intervals];
307     num_intervals += blob_info.blob->getIntercepts(
308         bounds_array, offset_intercepts_buffer, &paint);
309   }
310   return num_intervals;
311 }
312 
GetTextInterceptsInternal(const ShapeResultBloberizer::BlobBuffer & blobs,const cc::PaintFlags & flags,const std::tuple<float,float> & bounds,Vector<Font::TextIntercept> & intercepts)313 void GetTextInterceptsInternal(const ShapeResultBloberizer::BlobBuffer& blobs,
314                                const cc::PaintFlags& flags,
315                                const std::tuple<float, float>& bounds,
316                                Vector<Font::TextIntercept>& intercepts) {
317   // Get the number of intervals, without copying the actual values by
318   // specifying nullptr for the buffer, following the Skia allocation model for
319   // retrieving text intercepts.
320   SkPaint paint = flags.ToSkPaint();
321   unsigned num_intervals = InterceptsFromBlobs(blobs, paint, bounds, nullptr);
322   if (!num_intervals)
323     return;
324   DCHECK_EQ(num_intervals % 2, 0u);
325   intercepts.resize(num_intervals / 2u);
326 
327   InterceptsFromBlobs(blobs, paint, bounds,
328                       reinterpret_cast<SkScalar*>(intercepts.data()));
329 }
330 
331 }  // anonymous namespace
332 
GetTextIntercepts(const TextRunPaintInfo & run_info,float device_scale_factor,const cc::PaintFlags & flags,const std::tuple<float,float> & bounds,Vector<TextIntercept> & intercepts) const333 void Font::GetTextIntercepts(const TextRunPaintInfo& run_info,
334                              float device_scale_factor,
335                              const cc::PaintFlags& flags,
336                              const std::tuple<float, float>& bounds,
337                              Vector<TextIntercept>& intercepts) const {
338   if (ShouldSkipDrawing())
339     return;
340 
341   ShapeResultBloberizer bloberizer(
342       *this, device_scale_factor, ShapeResultBloberizer::Type::kTextIntercepts);
343   CachingWordShaper word_shaper(*this);
344   ShapeResultBuffer buffer;
345   word_shaper.FillResultBuffer(run_info, &buffer);
346   bloberizer.FillGlyphs(run_info, buffer);
347 
348   GetTextInterceptsInternal(bloberizer.Blobs(), flags, bounds, intercepts);
349 }
350 
GetTextIntercepts(const NGTextFragmentPaintInfo & text_info,float device_scale_factor,const cc::PaintFlags & flags,const std::tuple<float,float> & bounds,Vector<TextIntercept> & intercepts) const351 void Font::GetTextIntercepts(const NGTextFragmentPaintInfo& text_info,
352                              float device_scale_factor,
353                              const cc::PaintFlags& flags,
354                              const std::tuple<float, float>& bounds,
355                              Vector<TextIntercept>& intercepts) const {
356   if (ShouldSkipDrawing())
357     return;
358 
359   ShapeResultBloberizer bloberizer(
360       *this, device_scale_factor, ShapeResultBloberizer::Type::kTextIntercepts);
361   bloberizer.FillGlyphs(text_info.text, text_info.from, text_info.to,
362                         text_info.shape_result);
363 
364   GetTextInterceptsInternal(bloberizer.Blobs(), flags, bounds, intercepts);
365 }
366 
PixelSnappedSelectionRect(FloatRect rect)367 static inline FloatRect PixelSnappedSelectionRect(FloatRect rect) {
368   // Using roundf() rather than ceilf() for the right edge as a compromise to
369   // ensure correct caret positioning.
370   float rounded_x = roundf(rect.X());
371   return FloatRect(rounded_x, rect.Y(), roundf(rect.MaxX() - rounded_x),
372                    rect.Height());
373 }
374 
SelectionRectForText(const TextRun & run,const FloatPoint & point,float height,int from,int to) const375 FloatRect Font::SelectionRectForText(const TextRun& run,
376                                      const FloatPoint& point,
377                                      float height,
378                                      int from,
379                                      int to) const {
380   to = (to == -1 ? run.length() : to);
381 
382   FontCachePurgePreventer purge_preventer;
383 
384   CachingWordShaper shaper(*this);
385   CharacterRange range = shaper.GetCharacterRange(run, from, to);
386 
387   return PixelSnappedSelectionRect(
388       FloatRect(point.X() + range.start, point.Y(), range.Width(), height));
389 }
390 
BoundingBox(const TextRun & run,int from,int to) const391 FloatRect Font::BoundingBox(const TextRun& run, int from, int to) const {
392   to = (to == -1 ? run.length() : to);
393   FontCachePurgePreventer purge_preventer;
394   CachingWordShaper shaper(*this);
395   CharacterRange range = shaper.GetCharacterRange(run, from, to);
396   return FloatRect(range.start, -range.ascent, range.Width(), range.Height());
397 }
398 
OffsetForPosition(const TextRun & run,float x_float,IncludePartialGlyphsOption partial_glyphs,BreakGlyphsOption break_glyphs) const399 int Font::OffsetForPosition(const TextRun& run,
400                             float x_float,
401                             IncludePartialGlyphsOption partial_glyphs,
402                             BreakGlyphsOption break_glyphs) const {
403   FontCachePurgePreventer purge_preventer;
404   CachingWordShaper shaper(*this);
405   return shaper.OffsetForPosition(run, x_float, partial_glyphs, break_glyphs);
406 }
407 
GetShapeCache() const408 ShapeCache* Font::GetShapeCache() const {
409   return EnsureFontFallbackList()->GetShapeCache(font_description_);
410 }
411 
CanShapeWordByWord() const412 bool Font::CanShapeWordByWord() const {
413   return EnsureFontFallbackList()->CanShapeWordByWord(GetFontDescription());
414 }
415 
ReportNotDefGlyph() const416 void Font::ReportNotDefGlyph() const {
417   FontSelector* fontSelector = EnsureFontFallbackList()->GetFontSelector();
418   // We have a few non-DOM usages of Font code, for example in DragImage::Create
419   // and in EmbeddedObjectPainter::paintReplaced. In those cases, we can't
420   // retrieve a font selector as our connection to a Document object to report
421   // UseCounter metrics, and thus we cannot report notdef glyphs.
422   if (fontSelector)
423     fontSelector->ReportNotDefGlyph();
424 }
425 
WillUseFontData(const String & text) const426 void Font::WillUseFontData(const String& text) const {
427   const FontFamily& family = GetFontDescription().Family();
428   if (font_fallback_list_ && font_fallback_list_->GetFontSelector() &&
429       !family.FamilyIsEmpty())
430     font_fallback_list_->GetFontSelector()->WillUseFontData(
431         GetFontDescription(), family.Family(), text);
432 }
433 
GetEmphasisMarkGlyphData(const AtomicString & mark) const434 GlyphData Font::GetEmphasisMarkGlyphData(const AtomicString& mark) const {
435   if (mark.IsEmpty())
436     return GlyphData();
437 
438   TextRun emphasis_mark_run(mark, mark.length());
439   return CachingWordShaper(*this).EmphasisMarkGlyphData(emphasis_mark_run);
440 }
441 
EmphasisMarkAscent(const AtomicString & mark) const442 int Font::EmphasisMarkAscent(const AtomicString& mark) const {
443   FontCachePurgePreventer purge_preventer;
444 
445   const auto mark_glyph_data = GetEmphasisMarkGlyphData(mark);
446   const SimpleFontData* mark_font_data = mark_glyph_data.font_data;
447   if (!mark_font_data)
448     return 0;
449 
450   return mark_font_data->GetFontMetrics().Ascent();
451 }
452 
EmphasisMarkDescent(const AtomicString & mark) const453 int Font::EmphasisMarkDescent(const AtomicString& mark) const {
454   FontCachePurgePreventer purge_preventer;
455 
456   const auto mark_glyph_data = GetEmphasisMarkGlyphData(mark);
457   const SimpleFontData* mark_font_data = mark_glyph_data.font_data;
458   if (!mark_font_data)
459     return 0;
460 
461   return mark_font_data->GetFontMetrics().Descent();
462 }
463 
EmphasisMarkHeight(const AtomicString & mark) const464 int Font::EmphasisMarkHeight(const AtomicString& mark) const {
465   FontCachePurgePreventer purge_preventer;
466 
467   const auto mark_glyph_data = GetEmphasisMarkGlyphData(mark);
468   const SimpleFontData* mark_font_data = mark_glyph_data.font_data;
469   if (!mark_font_data)
470     return 0;
471 
472   return mark_font_data->GetFontMetrics().Height();
473 }
474 
GetCharacterRange(const TextRun & run,unsigned from,unsigned to) const475 CharacterRange Font::GetCharacterRange(const TextRun& run,
476                                        unsigned from,
477                                        unsigned to) const {
478   FontCachePurgePreventer purge_preventer;
479   CachingWordShaper shaper(*this);
480   return shaper.GetCharacterRange(run, from, to);
481 }
482 
IndividualCharacterRanges(const TextRun & run) const483 Vector<CharacterRange> Font::IndividualCharacterRanges(
484     const TextRun& run) const {
485   FontCachePurgePreventer purge_preventer;
486   CachingWordShaper shaper(*this);
487   auto ranges = shaper.IndividualCharacterRanges(run);
488   // The shaper should return ranges.size == run.length but on some platforms
489   // (OSX10.9.5) we are seeing cases in the upper end of the unicode range
490   // where this is not true (see: crbug.com/620952). To catch these cases on
491   // more popular platforms, and to protect users, we are using a CHECK here.
492   CHECK_EQ(ranges.size(), run.length());
493   return ranges;
494 }
495 
IndividualCharacterAdvances(const TextRun & run) const496 Vector<double> Font::IndividualCharacterAdvances(const TextRun& run) const {
497   FontCachePurgePreventer purge_preventer;
498   CachingWordShaper shaper(*this);
499   return shaper.IndividualCharacterAdvances(run);
500 }
501 
ExpandRangeToIncludePartialGlyphs(const TextRun & text_run,int * from,int * to) const502 void Font::ExpandRangeToIncludePartialGlyphs(const TextRun& text_run,
503                                              int* from,
504                                              int* to) const {
505   TextRunPaintInfo run_info(text_run);
506   run_info.from = *from;
507   run_info.to = *to;
508   CachingWordShaper word_shaper(*this);
509   ShapeResultBuffer buffer;
510   word_shaper.FillResultBuffer(run_info, &buffer);
511   buffer.ExpandRangeToIncludePartialGlyphs(from, to);
512 }
513 
TabWidth(const SimpleFontData * font_data,const TabSize & tab_size,float position) const514 float Font::TabWidth(const SimpleFontData* font_data,
515                      const TabSize& tab_size,
516                      float position) const {
517   float base_tab_width = TabWidth(font_data, tab_size);
518   if (!base_tab_width)
519     return GetFontDescription().LetterSpacing();
520 
521   float distance_to_tab_stop = base_tab_width - fmodf(position, base_tab_width);
522 
523   // Let the minimum width be the half of the space width so that it's always
524   // recognizable.  if the distance to the next tab stop is less than that,
525   // advance an additional tab stop.
526   if (distance_to_tab_stop < font_data->SpaceWidth() / 2)
527     distance_to_tab_stop += base_tab_width;
528 
529   return distance_to_tab_stop;
530 }
531 
TabWidth(const TabSize & tab_size,LayoutUnit position) const532 LayoutUnit Font::TabWidth(const TabSize& tab_size, LayoutUnit position) const {
533   const SimpleFontData* font_data = PrimaryFont();
534   if (!font_data)
535     return LayoutUnit::FromFloatCeil(GetFontDescription().LetterSpacing());
536   float base_tab_width = tab_size.GetPixelSize(font_data->SpaceWidth());
537   if (!base_tab_width)
538     return LayoutUnit::FromFloatCeil(GetFontDescription().LetterSpacing());
539 
540   LayoutUnit distance_to_tab_stop = LayoutUnit::FromFloatFloor(
541       base_tab_width - fmodf(position, base_tab_width));
542 
543   // Let the minimum width be the half of the space width so that it's always
544   // recognizable.  if the distance to the next tab stop is less than that,
545   // advance an additional tab stop.
546   if (distance_to_tab_stop < font_data->SpaceWidth() / 2)
547     distance_to_tab_stop += base_tab_width;
548 
549   return distance_to_tab_stop;
550 }
551 
LoadingCustomFonts() const552 bool Font::LoadingCustomFonts() const {
553   return font_fallback_list_ && font_fallback_list_->LoadingCustomFonts();
554 }
555 
IsFallbackValid() const556 bool Font::IsFallbackValid() const {
557   return !font_fallback_list_ || font_fallback_list_->IsValid();
558 }
559 
560 }  // namespace blink
561