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