render_text_harfbuzz.cc 90 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/gfx/render_text_harfbuzz.h"
  5. #include <limits>
  6. #include <set>
  7. #include "base/command_line.h"
  8. #include "base/containers/contains.h"
  9. #include "base/containers/lru_cache.h"
  10. #include "base/containers/span.h"
  11. #include "base/feature_list.h"
  12. #include "base/hash/hash.h"
  13. #include "base/i18n/base_i18n_switches.h"
  14. #include "base/i18n/break_iterator.h"
  15. #include "base/i18n/char_iterator.h"
  16. #include "base/i18n/rtl.h"
  17. #include "base/logging.h"
  18. #include "base/memory/ptr_util.h"
  19. #include "base/memory/raw_ptr.h"
  20. #include "base/metrics/histogram_macros.h"
  21. #include "base/no_destructor.h"
  22. #include "base/numerics/safe_conversions.h"
  23. #include "base/strings/string_number_conversions.h"
  24. #include "base/strings/string_piece.h"
  25. #include "base/strings/string_util.h"
  26. #include "base/strings/utf_string_conversions.h"
  27. #include "base/task/current_thread.h"
  28. #include "base/trace_event/trace_event.h"
  29. #include "build/build_config.h"
  30. #include "third_party/icu/source/common/unicode/ubidi.h"
  31. #include "third_party/icu/source/common/unicode/uscript.h"
  32. #include "third_party/icu/source/common/unicode/utf16.h"
  33. #include "third_party/skia/include/core/SkColor.h"
  34. #include "third_party/skia/include/core/SkFontMetrics.h"
  35. #include "third_party/skia/include/core/SkTypeface.h"
  36. #include "ui/gfx/bidi_line_iterator.h"
  37. #include "ui/gfx/canvas.h"
  38. #include "ui/gfx/decorated_text.h"
  39. #include "ui/gfx/font.h"
  40. #include "ui/gfx/font_fallback.h"
  41. #include "ui/gfx/font_render_params.h"
  42. #include "ui/gfx/harfbuzz_font_skia.h"
  43. #include "ui/gfx/platform_font.h"
  44. #include "ui/gfx/range/range_f.h"
  45. #include "ui/gfx/skia_util.h"
  46. #include "ui/gfx/switches.h"
  47. #include "ui/gfx/text_utils.h"
  48. #include "ui/gfx/utf16_indexing.h"
  49. #if BUILDFLAG(IS_APPLE)
  50. #include "base/mac/foundation_util.h"
  51. #include "base/mac/mac_util.h"
  52. #include "third_party/skia/include/ports/SkTypeface_mac.h"
  53. #endif
  54. #if BUILDFLAG(IS_ANDROID)
  55. #include "base/android/locale_utils.h"
  56. #endif // BUILDFLAG(IS_ANDROID)
  57. #include <hb.h>
  58. namespace gfx {
  59. namespace {
  60. // Text length limit. Longer strings are slow and not fully tested.
  61. const size_t kMaxTextLength = 10000;
  62. // The maximum number of scripts a Unicode character can belong to. This value
  63. // is arbitrarily chosen to be a good limit because it is unlikely for a single
  64. // character to belong to more scripts.
  65. const size_t kMaxScripts = 32;
  66. // Font fallback mechanism used to Shape runs (see ShapeRuns(...)).
  67. // These values are persisted to logs. Entries should not be renumbered and
  68. // numeric values should never be reused.
  69. enum class ShapeRunFallback {
  70. FAILED = 0,
  71. NO_FALLBACK = 1,
  72. FALLBACK = 2,
  73. FALLBACKS = 3,
  74. kMaxValue = FALLBACKS
  75. };
  76. // Log the fallback font mechanism used for shaping to UMA (see ShapeRuns(...)).
  77. void RecordShapeRunsFallback(ShapeRunFallback fallback) {
  78. UMA_HISTOGRAM_ENUMERATION("RenderTextHarfBuzz.ShapeRunsFallback", fallback);
  79. }
  80. // Returns whether the codepoint has the 'extended pictographic' property.
  81. bool IsExtendedPictographicCodepoint(UChar32 codepoint) {
  82. return u_hasBinaryProperty(codepoint, UCHAR_EXTENDED_PICTOGRAPHIC);
  83. }
  84. // Returns whether the codepoint has emoji properties.
  85. bool IsEmojiRelatedCodepoint(UChar32 codepoint) {
  86. return u_hasBinaryProperty(codepoint, UCHAR_EMOJI) ||
  87. u_hasBinaryProperty(codepoint, UCHAR_EMOJI_PRESENTATION) ||
  88. u_hasBinaryProperty(codepoint, UCHAR_REGIONAL_INDICATOR);
  89. }
  90. // Returns true if |codepoint| is a bracket. This is used to avoid "matching"
  91. // brackets picking different font fallbacks, thereby appearing mismatched.
  92. bool IsBracket(UChar32 codepoint) {
  93. return u_getIntPropertyValue(codepoint, UCHAR_BIDI_PAIRED_BRACKET_TYPE) !=
  94. U_BPT_NONE;
  95. }
  96. // Writes the script and the script extensions of the Unicode |codepoint|.
  97. // Returns the number of written scripts.
  98. size_t GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) {
  99. // Fill |scripts| with the script extensions.
  100. UErrorCode icu_error = U_ZERO_ERROR;
  101. size_t count =
  102. uscript_getScriptExtensions(codepoint, scripts, kMaxScripts, &icu_error);
  103. DCHECK_NE(icu_error, U_BUFFER_OVERFLOW_ERROR) << " #ext: " << count;
  104. if (U_FAILURE(icu_error))
  105. return 0;
  106. return count;
  107. }
  108. // Intersects the script extensions set of |codepoint| with |result| and writes
  109. // to |result|, reading and updating |result_size|. The output |result| will be
  110. // a subset of the input |result| (thus |result_size| can only be smaller).
  111. void ScriptSetIntersect(UChar32 codepoint,
  112. UScriptCode* result,
  113. size_t* result_size) {
  114. // Each codepoint has a Script property and a Script Extensions (Scx)
  115. // property.
  116. //
  117. // The implicit Script property values 'Common' and 'Inherited' indicate that
  118. // a codepoint is widely used in many scripts, rather than being associated
  119. // to a specific script.
  120. //
  121. // However, some codepoints that are assigned a value of 'Common' or
  122. // 'Inherited' are not commonly used with all scripts, but rather only with a
  123. // limited set of scripts. The Script Extension property is used to specify
  124. // the set of script which borrow the codepoint.
  125. //
  126. // Calls to GetScriptExtensions(...) return the set of scripts where the
  127. // codepoints can be used.
  128. // (see table 7 from http://www.unicode.org/reports/tr24/tr24-29.html)
  129. //
  130. // Script Script Extensions -> Results
  131. // 1) Common {Common} -> {Common}
  132. // Inherited {Inherited} -> {Inherited}
  133. // 2) Latin {Latn} -> {Latn}
  134. // Inherited {Latn} -> {Latn}
  135. // 3) Common {Hira Kana} -> {Hira Kana}
  136. // Inherited {Hira Kana} -> {Hira Kana}
  137. // 4) Devanagari {Deva Dogr Kthi Mahj} -> {Deva Dogr Kthi Mahj}
  138. // Myanmar {Cakm Mymr Tale} -> {Cakm Mymr Tale}
  139. //
  140. // For most of the codepoints, the script extensions set contains only one
  141. // element. For CJK codepoints, it's common to see 3-4 scripts. For really
  142. // rare cases, the set can go above 20 scripts.
  143. UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE };
  144. size_t count = GetScriptExtensions(codepoint, scripts);
  145. // Implicit script 'inherited' is inheriting scripts from preceding codepoint.
  146. if (count == 1 && scripts[0] == USCRIPT_INHERITED)
  147. return;
  148. // Perform the intersection of both script set.
  149. auto scripts_span = base::span<UScriptCode>(scripts, count);
  150. DCHECK(!base::Contains(scripts_span, USCRIPT_INHERITED));
  151. auto results_span = base::span<UScriptCode>(result, *result_size);
  152. size_t out_size = 0;
  153. for (UScriptCode current : results_span) {
  154. if (base::Contains(scripts_span, current))
  155. result[out_size++] = current;
  156. }
  157. *result_size = out_size;
  158. }
  159. struct GraphemeProperties {
  160. bool has_control = false;
  161. bool has_bracket = false;
  162. bool has_pictographic = false;
  163. bool has_emoji = false;
  164. UBlockCode block = UBLOCK_NO_BLOCK;
  165. };
  166. // Returns the properties for the codepoints part of the given text.
  167. GraphemeProperties RetrieveGraphemeProperties(const base::StringPiece16& text,
  168. bool retrieve_block) {
  169. GraphemeProperties properties;
  170. bool first_char = true;
  171. for (base::i18n::UTF16CharIterator iter(text); !iter.end(); iter.Advance()) {
  172. const UChar32 codepoint = iter.get();
  173. if (first_char) {
  174. first_char = false;
  175. if (retrieve_block)
  176. properties.block = ublock_getCode(codepoint);
  177. }
  178. if (codepoint == '\n' || codepoint == '\r' || codepoint == ' ')
  179. properties.has_control = true;
  180. if (IsBracket(codepoint))
  181. properties.has_bracket = true;
  182. if (IsExtendedPictographicCodepoint(codepoint))
  183. properties.has_pictographic = true;
  184. if (IsEmojiRelatedCodepoint(codepoint))
  185. properties.has_emoji = true;
  186. }
  187. return properties;
  188. }
  189. // Return whether the grapheme properties are compatible and the grapheme can
  190. // be merge together in the same grapheme cluster.
  191. bool AreGraphemePropertiesCompatible(const GraphemeProperties& first,
  192. const GraphemeProperties& second) {
  193. // There are 5 constrains to grapheme to be compatible.
  194. // 1) The newline character and control characters should form a single run so
  195. // that the line breaker can handle them easily.
  196. // 2) Parentheses should be put in a separate run to avoid using different
  197. // fonts while rendering matching parentheses (see http://crbug.com/396776).
  198. // 3) Pictographic graphemes should be put in separate run to avoid altering
  199. // fonts selection while rendering adjacent text (see
  200. // http://crbug.com/278913).
  201. // 4) Emoji graphemes should be put in separate run (see
  202. // http://crbug.com/530021 and http://crbug.com/533721).
  203. // 5) The 'COMMON' script needs to be split by unicode block. Codepoints are
  204. // spread across blocks and supported with different fonts.
  205. return !first.has_control && !second.has_control &&
  206. first.has_bracket == second.has_bracket &&
  207. first.has_pictographic == second.has_pictographic &&
  208. first.has_emoji == second.has_emoji && first.block == second.block;
  209. }
  210. // Returns the end of the current grapheme cluster. This function is finding the
  211. // breaking point where grapheme properties are no longer compatible
  212. // (see: UNICODE TEXT SEGMENTATION (http://unicode.org/reports/tr29/).
  213. // Breaks between |run_start| and |run_end| and force break after the grapheme
  214. // starting at |run_break|.
  215. size_t FindRunBreakingCharacter(const std::u16string& text,
  216. UScriptCode script,
  217. size_t run_start,
  218. size_t run_break,
  219. size_t run_end) {
  220. const size_t run_length = run_end - run_start;
  221. const base::StringPiece16 run_text(text.c_str() + run_start, run_length);
  222. const bool is_common_script = (script == USCRIPT_COMMON);
  223. DCHECK(!run_text.empty());
  224. // Create an iterator to split the text in graphemes.
  225. base::i18n::BreakIterator grapheme_iterator(
  226. run_text, base::i18n::BreakIterator::BREAK_CHARACTER);
  227. if (!grapheme_iterator.Init() || !grapheme_iterator.Advance()) {
  228. // In case of error, isolate the first character in a separate run.
  229. NOTREACHED();
  230. return run_start + 1;
  231. }
  232. // Retrieve the first grapheme and its codepoint properties.
  233. const base::StringPiece16 first_grapheme_text =
  234. grapheme_iterator.GetStringPiece();
  235. const GraphemeProperties first_grapheme_properties =
  236. RetrieveGraphemeProperties(first_grapheme_text, is_common_script);
  237. // Append subsequent graphemes in this grapheme cluster if they are
  238. // compatible, otherwise break the current run.
  239. while (grapheme_iterator.Advance()) {
  240. const base::StringPiece16 current_grapheme_text =
  241. grapheme_iterator.GetStringPiece();
  242. const GraphemeProperties current_grapheme_properties =
  243. RetrieveGraphemeProperties(current_grapheme_text, is_common_script);
  244. const size_t current_breaking_position =
  245. run_start + grapheme_iterator.prev();
  246. if (!AreGraphemePropertiesCompatible(first_grapheme_properties,
  247. current_grapheme_properties)) {
  248. return current_breaking_position;
  249. }
  250. // Break if the beginning of this grapheme is after |run_break|.
  251. if (run_start + grapheme_iterator.prev() >= run_break) {
  252. DCHECK_LE(current_breaking_position, run_end);
  253. return current_breaking_position;
  254. }
  255. }
  256. // Do not break this run, returns end of the text.
  257. return run_end;
  258. }
  259. // Find the longest sequence of characters from 0 and up to |length| that have
  260. // at least one common UScriptCode value. Writes the common script value to
  261. // |script| and returns the length of the sequence. Takes the characters' script
  262. // extensions into account. http://www.unicode.org/reports/tr24/#ScriptX
  263. //
  264. // Consider 3 characters with the script values {Kana}, {Hira, Kana}, {Kana}.
  265. // Without script extensions only the first script in each set would be taken
  266. // into account, resulting in 3 runs where 1 would be enough.
  267. size_t ScriptInterval(const std::u16string& text,
  268. size_t start,
  269. size_t length,
  270. UScriptCode* script) {
  271. DCHECK_GT(length, 0U);
  272. UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE };
  273. base::i18n::UTF16CharIterator char_iterator(
  274. base::StringPiece16(text.c_str() + start, length));
  275. size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts);
  276. *script = scripts[0];
  277. while (char_iterator.Advance()) {
  278. ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size);
  279. if (scripts_size == 0U)
  280. return char_iterator.array_pos();
  281. *script = scripts[0];
  282. }
  283. return length;
  284. }
  285. // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without
  286. // hb-icu. See http://crbug.com/356929
  287. inline hb_script_t ICUScriptToHBScript(UScriptCode script) {
  288. if (script == USCRIPT_INVALID_CODE)
  289. return HB_SCRIPT_INVALID;
  290. return hb_script_from_string(uscript_getShortName(script), -1);
  291. }
  292. bool FontWasAlreadyTried(sk_sp<SkTypeface> typeface,
  293. std::set<SkFontID>* fallback_fonts) {
  294. return fallback_fonts->count(typeface->uniqueID()) != 0;
  295. }
  296. void MarkFontAsTried(sk_sp<SkTypeface> typeface,
  297. std::set<SkFontID>* fallback_fonts) {
  298. fallback_fonts->insert(typeface->uniqueID());
  299. }
  300. // Whether |segment| corresponds to the newline character.
  301. bool IsNewlineSegment(const std::u16string& text,
  302. const internal::LineSegment& segment) {
  303. const size_t offset = segment.char_range.start();
  304. const size_t length = segment.char_range.length();
  305. DCHECK_LT(segment.char_range.start() + length - 1, text.length());
  306. return (length == 1 && (text[offset] == '\r' || text[offset] == '\n')) ||
  307. (length == 2 && text[offset] == '\r' && text[offset + 1] == '\n');
  308. }
  309. // Returns the line index considering the newline character. Line index is
  310. // incremented if the caret is right after the newline character, i.e, the
  311. // cursor affinity is |CURSOR_BACKWARD| while containing the newline character.
  312. size_t LineIndexForNewline(const size_t line_index,
  313. const std::u16string& text,
  314. const internal::LineSegment& segment,
  315. const SelectionModel& caret) {
  316. bool at_newline = IsNewlineSegment(text, segment) &&
  317. caret.caret_affinity() == CURSOR_BACKWARD;
  318. return line_index + (at_newline ? 1 : 0);
  319. }
  320. // Helper template function for |TextRunHarfBuzz::GetClusterAt()|. |Iterator|
  321. // can be a forward or reverse iterator type depending on the text direction.
  322. // Returns true on success, or false if an error is encountered.
  323. template <class Iterator>
  324. bool GetClusterAtImpl(size_t pos,
  325. Range range,
  326. Iterator elements_begin,
  327. Iterator elements_end,
  328. bool reversed,
  329. Range* chars,
  330. Range* glyphs) {
  331. Iterator element = std::upper_bound(elements_begin, elements_end, pos);
  332. if (element == elements_begin) {
  333. *chars = range;
  334. *glyphs = Range();
  335. return false;
  336. }
  337. chars->set_end(element == elements_end ? range.end() : *element);
  338. glyphs->set_end(reversed ? elements_end - element : element - elements_begin);
  339. while (--element != elements_begin && *element == *(element - 1));
  340. chars->set_start(*element);
  341. glyphs->set_start(
  342. reversed ? elements_end - element : element - elements_begin);
  343. if (reversed)
  344. *glyphs = Range(glyphs->end(), glyphs->start());
  345. DCHECK(!chars->is_reversed());
  346. DCHECK(!chars->is_empty());
  347. DCHECK(!glyphs->is_reversed());
  348. DCHECK(!glyphs->is_empty());
  349. return true;
  350. }
  351. // Internal class to generate Line structures. If |multiline| is true, the text
  352. // is broken into lines at |words| boundaries such that each line is no longer
  353. // than |max_width|. If |multiline| is false, only outputs a single Line from
  354. // the given runs. |min_baseline| and |min_height| are the minimum baseline and
  355. // height for each line.
  356. // TODO(ckocagil): Expose the interface of this class in the header and test
  357. // this class directly.
  358. class HarfBuzzLineBreaker {
  359. public:
  360. HarfBuzzLineBreaker(size_t max_width,
  361. int min_baseline,
  362. float min_height,
  363. float glyph_height_for_test,
  364. WordWrapBehavior word_wrap_behavior,
  365. const std::u16string& text,
  366. const internal::TextRunList& run_list)
  367. : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)),
  368. min_baseline_(min_baseline),
  369. min_height_(min_height),
  370. glyph_height_for_test_(glyph_height_for_test),
  371. word_wrap_behavior_(word_wrap_behavior),
  372. text_(text),
  373. run_list_(run_list),
  374. max_descent_(0),
  375. max_ascent_(0),
  376. text_x_(0),
  377. available_width_(max_width_) {
  378. AdvanceLine();
  379. }
  380. HarfBuzzLineBreaker(const HarfBuzzLineBreaker&) = delete;
  381. HarfBuzzLineBreaker& operator=(const HarfBuzzLineBreaker&) = delete;
  382. // Constructs a single line for |text_| using |run_list_|.
  383. void ConstructSingleLine() {
  384. for (size_t i = 0; i < run_list_.size(); i++) {
  385. const internal::TextRunHarfBuzz& run = *(run_list_.runs()[i]);
  386. internal::LineSegment segment;
  387. segment.run = i;
  388. segment.char_range = run.range;
  389. segment.x_range = RangeF(SkScalarToFloat(text_x_),
  390. SkScalarToFloat(text_x_) + run.shape.width);
  391. AddLineSegment(segment, false);
  392. }
  393. }
  394. // Constructs multiple lines for |text_| based on words iteration approach.
  395. void ConstructMultiLines() {
  396. // Get an iterator that pass through valid line breaking positions.
  397. // See https://www.unicode.org/reports/tr14/tr14-11.html for lines breaking.
  398. base::i18n::BreakIterator words(text_,
  399. base::i18n::BreakIterator::BREAK_LINE);
  400. const bool success = words.Init();
  401. DCHECK(success);
  402. if (!success)
  403. return;
  404. while (words.Advance()) {
  405. const Range word_range = Range(words.prev(), words.pos());
  406. std::vector<internal::LineSegment> word_segments;
  407. SkScalar word_width = GetWordWidth(word_range, &word_segments);
  408. // If the last word is '\n', we should advance a new line after adding
  409. // the word to the current line.
  410. bool new_line = false;
  411. if (!word_segments.empty() &&
  412. IsNewlineSegment(text_, word_segments.back())) {
  413. new_line = true;
  414. // Subtract the width of newline segments, they are not drawn.
  415. if (word_segments.size() != 1u || available_width_ != max_width_)
  416. word_width -= word_segments.back().width();
  417. }
  418. // If the word is not the first word in the line and it can't fit into
  419. // the current line, advance a new line.
  420. if (word_width > available_width_ && available_width_ != max_width_)
  421. AdvanceLine();
  422. if (!word_segments.empty())
  423. AddWordToLine(word_segments);
  424. if (new_line)
  425. AdvanceLine();
  426. }
  427. }
  428. // Finishes line breaking and outputs the results. Can be called at most once.
  429. void FinalizeLines(std::vector<internal::Line>* lines, SizeF* size) {
  430. DCHECK(!lines_.empty());
  431. // If the last character of the text is a new line character, then the last
  432. // line is any empty string, which contains no segments. This means that the
  433. // display_text_index will not have been set in AdvanceLine. So here, set
  434. // display_text_index to the text length, which is the true text index of
  435. // the final line.
  436. internal::Line* line = &lines_.back();
  437. if (line->display_text_index == 0)
  438. line->display_text_index = text_.size();
  439. // Add an empty line to finish the line size calculation and remove it.
  440. AdvanceLine();
  441. lines_.pop_back();
  442. *size = total_size_;
  443. lines->swap(lines_);
  444. }
  445. private:
  446. // A (line index, segment index) pair that specifies a segment in |lines_|.
  447. typedef std::pair<size_t, size_t> SegmentHandle;
  448. internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) {
  449. return &lines_[handle.first].segments[handle.second];
  450. }
  451. // Finishes the size calculations of the last Line in |lines_|. Adds a new
  452. // Line to the back of |lines_|.
  453. void AdvanceLine() {
  454. if (!lines_.empty()) {
  455. internal::Line* line = &lines_.back();
  456. // Compute the line start while the line segments are in the logical order
  457. // so that the start of the line is the start of the char range,
  458. // regardless of i18n.
  459. if (!line->segments.empty())
  460. line->display_text_index = line->segments[0].char_range.start();
  461. std::sort(line->segments.begin(), line->segments.end(),
  462. [this](const internal::LineSegment& s1,
  463. const internal::LineSegment& s2) -> bool {
  464. return run_list_.logical_to_visual(s1.run) <
  465. run_list_.logical_to_visual(s2.run);
  466. });
  467. line->size.set_height(
  468. glyph_height_for_test_
  469. ? glyph_height_for_test_
  470. : std::max(min_height_, max_descent_ + max_ascent_));
  471. line->baseline = std::max(min_baseline_, SkScalarRoundToInt(max_ascent_));
  472. line->preceding_heights = base::ClampCeil(total_size_.height());
  473. // Subtract newline segment's width from |total_size_| because it's not
  474. // drawn.
  475. float line_width = line->size.width();
  476. if (!line->segments.empty() &&
  477. IsNewlineSegment(text_, line->segments.back())) {
  478. line_width -= line->segments.back().width();
  479. }
  480. if (line->segments.size() > 1 &&
  481. IsNewlineSegment(text_, line->segments.front())) {
  482. line_width -= line->segments.front().width();
  483. }
  484. total_size_.set_height(total_size_.height() + line->size.height());
  485. total_size_.set_width(std::max(total_size_.width(), line_width));
  486. }
  487. max_descent_ = 0;
  488. max_ascent_ = 0;
  489. available_width_ = max_width_;
  490. lines_.push_back(internal::Line());
  491. }
  492. // Adds word to the current line. A word may contain multiple segments. If the
  493. // word is the first word in line and its width exceeds |available_width_|,
  494. // ignore/truncate/wrap it according to |word_wrap_behavior_|.
  495. void AddWordToLine(const std::vector<internal::LineSegment>& word_segments) {
  496. DCHECK(!lines_.empty());
  497. DCHECK(!word_segments.empty());
  498. bool has_truncated = false;
  499. for (const internal::LineSegment& segment : word_segments) {
  500. if (has_truncated)
  501. break;
  502. if (IsNewlineSegment(text_, segment) ||
  503. segment.width() <= available_width_ ||
  504. word_wrap_behavior_ == IGNORE_LONG_WORDS) {
  505. AddLineSegment(segment, true);
  506. } else {
  507. DCHECK(word_wrap_behavior_ == TRUNCATE_LONG_WORDS ||
  508. word_wrap_behavior_ == WRAP_LONG_WORDS);
  509. has_truncated = (word_wrap_behavior_ == TRUNCATE_LONG_WORDS);
  510. const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
  511. internal::LineSegment remaining_segment = segment;
  512. while (!remaining_segment.char_range.is_empty()) {
  513. size_t cutoff_pos = GetCutoffPos(remaining_segment);
  514. SkScalar width = run.GetGlyphWidthForCharRange(
  515. Range(remaining_segment.char_range.start(), cutoff_pos));
  516. if (width > 0) {
  517. internal::LineSegment cut_segment;
  518. cut_segment.run = remaining_segment.run;
  519. cut_segment.char_range =
  520. Range(remaining_segment.char_range.start(), cutoff_pos);
  521. cut_segment.x_range = RangeF(SkScalarToFloat(text_x_),
  522. SkScalarToFloat(text_x_ + width));
  523. AddLineSegment(cut_segment, true);
  524. // Updates old segment range.
  525. remaining_segment.char_range.set_start(cutoff_pos);
  526. remaining_segment.x_range.set_start(SkScalarToFloat(text_x_));
  527. }
  528. if (has_truncated)
  529. break;
  530. if (!remaining_segment.char_range.is_empty())
  531. AdvanceLine();
  532. }
  533. }
  534. }
  535. }
  536. // Add a line segment to the current line. Note that, in order to keep the
  537. // visual order correct for ltr and rtl language, we need to merge segments
  538. // that belong to the same run.
  539. void AddLineSegment(const internal::LineSegment& segment, bool multiline) {
  540. DCHECK(!lines_.empty());
  541. internal::Line* line = &lines_.back();
  542. const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
  543. if (!line->segments.empty()) {
  544. internal::LineSegment& last_segment = line->segments.back();
  545. // Merge segments that belong to the same run.
  546. if (last_segment.run == segment.run) {
  547. DCHECK_EQ(last_segment.char_range.end(), segment.char_range.start());
  548. // Check there is less than a pixel between one run and the next.
  549. DCHECK_LE(
  550. std::abs(last_segment.x_range.end() - segment.x_range.start()),
  551. 1.0f);
  552. last_segment.char_range.set_end(segment.char_range.end());
  553. last_segment.x_range.set_end(SkScalarToFloat(text_x_) +
  554. segment.width());
  555. if (run.font_params.is_rtl &&
  556. last_segment.char_range.end() == run.range.end())
  557. UpdateRTLSegmentRanges();
  558. line->size.set_width(line->size.width() + segment.width());
  559. text_x_ += segment.width();
  560. available_width_ -= segment.width();
  561. return;
  562. }
  563. }
  564. line->segments.push_back(segment);
  565. line->size.set_width(line->size.width() + segment.width());
  566. // Newline characters are not drawn for multi-line, ignore their metrics.
  567. if (!multiline || !IsNewlineSegment(text_, segment)) {
  568. SkFont font(run.font_params.skia_face, run.font_params.font_size);
  569. font.setEdging(run.font_params.render_params.antialiasing
  570. ? SkFont::Edging::kAntiAlias
  571. : SkFont::Edging::kAlias);
  572. SkFontMetrics metrics;
  573. font.getMetrics(&metrics);
  574. // max_descent_ is y-down, fDescent is y-down, baseline_offset is y-down
  575. max_descent_ = std::max(
  576. max_descent_, metrics.fDescent + run.font_params.baseline_offset);
  577. // max_ascent_ is y-up, fAscent is y-down, baseline_offset is y-down
  578. max_ascent_ = std::max(
  579. max_ascent_, -(metrics.fAscent + run.font_params.baseline_offset));
  580. }
  581. if (run.font_params.is_rtl) {
  582. rtl_segments_.push_back(
  583. SegmentHandle(lines_.size() - 1, line->segments.size() - 1));
  584. // If this is the last segment of an RTL run, reprocess the text-space x
  585. // ranges of all segments from the run.
  586. if (segment.char_range.end() == run.range.end())
  587. UpdateRTLSegmentRanges();
  588. }
  589. text_x_ += segment.width();
  590. available_width_ -= segment.width();
  591. }
  592. // Finds the end position |end_pos| in |segment| where the preceding width is
  593. // no larger than |available_width_|.
  594. size_t GetCutoffPos(const internal::LineSegment& segment) const {
  595. DCHECK(!segment.char_range.is_empty());
  596. const internal::TextRunHarfBuzz& run =
  597. *(run_list_.runs()[segment.run]).get();
  598. size_t end_pos = segment.char_range.start();
  599. SkScalar width = 0;
  600. while (end_pos < segment.char_range.end()) {
  601. const SkScalar char_width =
  602. run.GetGlyphWidthForCharRange(Range(end_pos, end_pos + 1));
  603. if (width + char_width > available_width_)
  604. break;
  605. width += char_width;
  606. end_pos++;
  607. }
  608. const size_t valid_end_pos = std::max(
  609. segment.char_range.start(), FindValidBoundaryBefore(text_, end_pos));
  610. if (end_pos != valid_end_pos) {
  611. end_pos = valid_end_pos;
  612. width = run.GetGlyphWidthForCharRange(
  613. Range(segment.char_range.start(), end_pos));
  614. }
  615. // |max_width_| might be smaller than a single character. In this case we
  616. // need to put at least one character in the line. Note that, we should
  617. // not separate surrogate pair or combining characters.
  618. // See RenderTextHarfBuzzTest.Multiline_MinWidth for an example.
  619. if (width == 0 && available_width_ == max_width_) {
  620. end_pos = std::min(segment.char_range.end(),
  621. FindValidBoundaryAfter(text_, end_pos + 1));
  622. }
  623. return end_pos;
  624. }
  625. // Gets the glyph width for |word_range|, and splits the |word| into different
  626. // segments based on its runs.
  627. SkScalar GetWordWidth(const Range& word_range,
  628. std::vector<internal::LineSegment>* segments) const {
  629. if (word_range.is_empty() || segments == nullptr)
  630. return 0;
  631. size_t run_start_index = run_list_.GetRunIndexAt(word_range.start());
  632. size_t run_end_index = run_list_.GetRunIndexAt(word_range.end() - 1);
  633. SkScalar width = 0;
  634. for (size_t i = run_start_index; i <= run_end_index; i++) {
  635. const internal::TextRunHarfBuzz& run = *(run_list_.runs()[i]);
  636. const Range char_range = run.range.Intersect(word_range);
  637. DCHECK(!char_range.is_empty());
  638. const SkScalar char_width = run.GetGlyphWidthForCharRange(char_range);
  639. width += char_width;
  640. internal::LineSegment segment;
  641. segment.run = i;
  642. segment.char_range = char_range;
  643. segment.x_range = RangeF(SkScalarToFloat(text_x_ + width - char_width),
  644. SkScalarToFloat(text_x_ + width));
  645. segments->push_back(segment);
  646. }
  647. return width;
  648. }
  649. // RTL runs are broken in logical order but displayed in visual order. To find
  650. // the text-space coordinate (where it would fall in a single-line text)
  651. // |x_range| of RTL segments, segment widths are applied in reverse order.
  652. // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
  653. void UpdateRTLSegmentRanges() {
  654. if (rtl_segments_.empty())
  655. return;
  656. float x = SegmentFromHandle(rtl_segments_[0])->x_range.start();
  657. for (size_t i = rtl_segments_.size(); i > 0; --i) {
  658. internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]);
  659. const float segment_width = segment->width();
  660. segment->x_range = RangeF(x, x + segment_width);
  661. x += segment_width;
  662. }
  663. rtl_segments_.clear();
  664. }
  665. const SkScalar max_width_;
  666. const int min_baseline_;
  667. const float min_height_;
  668. const float glyph_height_for_test_;
  669. const WordWrapBehavior word_wrap_behavior_;
  670. const std::u16string& text_;
  671. const internal::TextRunList& run_list_;
  672. // Stores the resulting lines.
  673. std::vector<internal::Line> lines_;
  674. float max_descent_;
  675. float max_ascent_;
  676. // Text space x coordinates of the next segment to be added.
  677. SkScalar text_x_;
  678. // Stores available width in the current line.
  679. SkScalar available_width_;
  680. // Size of the multiline text, not including the currently processed line.
  681. SizeF total_size_;
  682. // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|.
  683. std::vector<SegmentHandle> rtl_segments_;
  684. };
  685. // Applies a forced text rendering direction if specified by a command-line
  686. // switch.
  687. void ApplyForcedDirection(UBiDiLevel* level) {
  688. static bool has_switch = base::CommandLine::ForCurrentProcess()->HasSwitch(
  689. switches::kForceTextDirection);
  690. if (!has_switch)
  691. return;
  692. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  693. if (command_line->HasSwitch(switches::kForceTextDirection)) {
  694. std::string force_flag =
  695. command_line->GetSwitchValueASCII(switches::kForceTextDirection);
  696. if (force_flag == switches::kForceDirectionRTL)
  697. *level = UBIDI_RTL;
  698. if (force_flag == switches::kForceDirectionLTR)
  699. *level = UBIDI_LTR;
  700. }
  701. }
  702. internal::TextRunHarfBuzz::FontParams CreateFontParams(
  703. const Font& primary_font,
  704. UBiDiLevel bidi_level,
  705. UScriptCode script,
  706. const internal::StyleIterator& style) {
  707. internal::TextRunHarfBuzz::FontParams font_params(primary_font);
  708. font_params.italic = style.style(TEXT_STYLE_ITALIC);
  709. font_params.baseline_type = style.baseline();
  710. font_params.font_size = style.font_size_override();
  711. font_params.strike = style.style(TEXT_STYLE_STRIKE);
  712. font_params.underline = style.style(TEXT_STYLE_UNDERLINE);
  713. font_params.heavy_underline = style.style(TEXT_STYLE_HEAVY_UNDERLINE);
  714. font_params.weight = style.weight();
  715. font_params.level = bidi_level;
  716. font_params.script = script;
  717. // Odd BiDi embedding levels correspond to RTL runs.
  718. font_params.is_rtl = (font_params.level % 2) == 1;
  719. return font_params;
  720. }
  721. } // namespace
  722. namespace internal {
  723. sk_sp<SkTypeface> CreateSkiaTypeface(const Font& font,
  724. bool italic,
  725. Font::Weight weight) {
  726. #if BUILDFLAG(IS_APPLE)
  727. const Font::FontStyle style = italic ? Font::ITALIC : Font::NORMAL;
  728. Font font_with_style = font.Derive(0, style, weight);
  729. if (!font_with_style.GetNativeFont())
  730. return nullptr;
  731. return SkMakeTypefaceFromCTFont(
  732. base::mac::NSToCFCast(font_with_style.GetNativeFont()));
  733. #else
  734. SkFontStyle skia_style(
  735. static_cast<int>(weight), SkFontStyle::kNormal_Width,
  736. italic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
  737. return sk_sp<SkTypeface>(SkTypeface::MakeFromName(
  738. font.GetFontName().c_str(), skia_style));
  739. #endif
  740. }
  741. TextRunHarfBuzz::FontParams::FontParams(const Font& template_font)
  742. : font(template_font) {}
  743. TextRunHarfBuzz::FontParams::~FontParams() = default;
  744. TextRunHarfBuzz::FontParams::FontParams(
  745. const TextRunHarfBuzz::FontParams& other) = default;
  746. TextRunHarfBuzz::FontParams& TextRunHarfBuzz::FontParams::operator=(
  747. const TextRunHarfBuzz::FontParams& other) = default;
  748. bool TextRunHarfBuzz::FontParams::operator==(const FontParams& other) const {
  749. // Empirically, |script| and |weight| are the highest entropy members.
  750. return script == other.script && weight == other.weight &&
  751. skia_face == other.skia_face && render_params == other.render_params &&
  752. font_size == other.font_size &&
  753. baseline_offset == other.baseline_offset &&
  754. baseline_type == other.baseline_type && italic == other.italic &&
  755. strike == other.strike && underline == other.underline &&
  756. heavy_underline == other.heavy_underline && is_rtl == other.is_rtl &&
  757. level == other.level;
  758. }
  759. void TextRunHarfBuzz::FontParams::
  760. ComputeRenderParamsFontSizeAndBaselineOffset() {
  761. render_params = font.GetFontRenderParams();
  762. if (font_size == 0)
  763. font_size = font.GetFontSize();
  764. baseline_offset = 0;
  765. if (baseline_type != NORMAL_BASELINE) {
  766. // Calculate a slightly smaller font. The ratio here is somewhat arbitrary.
  767. // Proportions from 5/9 to 5/7 all look pretty good.
  768. const float ratio = 5.0f / 9.0f;
  769. font_size = base::ClampRound(font.GetFontSize() * ratio);
  770. switch (baseline_type) {
  771. case SUPERSCRIPT:
  772. baseline_offset = font.GetCapHeight() - font.GetHeight();
  773. break;
  774. case SUPERIOR:
  775. baseline_offset =
  776. base::ClampRound(font.GetCapHeight() * ratio) - font.GetCapHeight();
  777. break;
  778. case SUBSCRIPT:
  779. baseline_offset = font.GetHeight() - font.GetBaseline();
  780. break;
  781. case INFERIOR: // Fall through.
  782. default:
  783. break;
  784. }
  785. }
  786. }
  787. size_t TextRunHarfBuzz::FontParams::Hash::operator()(
  788. const FontParams& key) const {
  789. // In practice, |font|, |skia_face|, |render_params|, and |baseline_offset|
  790. // have not yet been set when this is called.
  791. return static_cast<size_t>(key.italic) << 0 ^
  792. static_cast<size_t>(key.strike) << 1 ^
  793. static_cast<size_t>(key.underline) << 2 ^
  794. static_cast<size_t>(key.heavy_underline) << 3 ^
  795. static_cast<size_t>(key.is_rtl) << 4 ^
  796. static_cast<size_t>(key.weight) << 8 ^
  797. static_cast<size_t>(key.font_size) << 12 ^
  798. static_cast<size_t>(key.baseline_type) << 16 ^
  799. static_cast<size_t>(key.level) << 20 ^
  800. static_cast<size_t>(key.script) << 24;
  801. }
  802. bool TextRunHarfBuzz::FontParams::SetRenderParamsRematchFont(
  803. const Font& new_font,
  804. const FontRenderParams& new_render_params) {
  805. // This takes the font family name from new_font, and calls
  806. // SkTypeface::makeFromName() with that family name and the style information
  807. // internal to this text run. So it triggers a new font match and looks for
  808. // adjacent fonts in the family. This works for styling, e.g. styling a run in
  809. // bold, italic or underline, but breaks font fallback in certain scenarios,
  810. // as the fallback font may be of a different weight and style than the run's
  811. // own, so this can lead to a failure of instantiating the correct fallback
  812. // font.
  813. sk_sp<SkTypeface> new_skia_face(
  814. internal::CreateSkiaTypeface(new_font, italic, weight));
  815. if (!new_skia_face)
  816. return false;
  817. skia_face = new_skia_face;
  818. font = new_font;
  819. render_params = new_render_params;
  820. return true;
  821. }
  822. bool TextRunHarfBuzz::FontParams::SetRenderParamsOverrideSkiaFaceFromFont(
  823. const Font& fallback_font,
  824. const FontRenderParams& new_render_params) {
  825. PlatformFont* platform_font = fallback_font.platform_font();
  826. sk_sp<SkTypeface> new_skia_face = platform_font->GetNativeSkTypeface();
  827. // If pass-through of the Skia native handle fails for PlatformFonts other
  828. // than PlatformFontSkia, perform rematching.
  829. if (!new_skia_face)
  830. return SetRenderParamsRematchFont(fallback_font, new_render_params);
  831. skia_face = new_skia_face;
  832. font = fallback_font;
  833. render_params = new_render_params;
  834. return true;
  835. }
  836. TextRunHarfBuzz::ShapeOutput::ShapeOutput() = default;
  837. TextRunHarfBuzz::ShapeOutput::~ShapeOutput() = default;
  838. TextRunHarfBuzz::ShapeOutput::ShapeOutput(
  839. const TextRunHarfBuzz::ShapeOutput& other) = default;
  840. TextRunHarfBuzz::ShapeOutput& TextRunHarfBuzz::ShapeOutput::operator=(
  841. const TextRunHarfBuzz::ShapeOutput& other) = default;
  842. TextRunHarfBuzz::ShapeOutput::ShapeOutput(
  843. TextRunHarfBuzz::ShapeOutput&& other) = default;
  844. TextRunHarfBuzz::ShapeOutput& TextRunHarfBuzz::ShapeOutput::operator=(
  845. TextRunHarfBuzz::ShapeOutput&& other) = default;
  846. TextRunHarfBuzz::TextRunHarfBuzz(const Font& template_font)
  847. : font_params(template_font) {}
  848. TextRunHarfBuzz::~TextRunHarfBuzz() {}
  849. Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const {
  850. DCHECK(range.Contains(char_range));
  851. DCHECK(!char_range.is_reversed());
  852. DCHECK(!char_range.is_empty());
  853. Range start_glyphs;
  854. Range end_glyphs;
  855. Range temp_range;
  856. GetClusterAt(char_range.start(), &temp_range, &start_glyphs);
  857. GetClusterAt(char_range.end() - 1, &temp_range, &end_glyphs);
  858. return font_params.is_rtl ? Range(end_glyphs.start(), start_glyphs.end())
  859. : Range(start_glyphs.start(), end_glyphs.end());
  860. }
  861. size_t TextRunHarfBuzz::CountMissingGlyphs() const {
  862. return shape.missing_glyph_count;
  863. }
  864. void TextRunHarfBuzz::GetClusterAt(size_t pos,
  865. Range* chars,
  866. Range* glyphs) const {
  867. DCHECK(chars);
  868. DCHECK(glyphs);
  869. bool success = true;
  870. if (shape.glyph_count == 0 || !range.Contains(Range(pos, pos + 1))) {
  871. *chars = range;
  872. *glyphs = Range();
  873. success = false;
  874. }
  875. if (font_params.is_rtl) {
  876. success &=
  877. GetClusterAtImpl(pos, range, shape.glyph_to_char.rbegin(),
  878. shape.glyph_to_char.rend(), true, chars, glyphs);
  879. } else {
  880. success &=
  881. GetClusterAtImpl(pos, range, shape.glyph_to_char.begin(),
  882. shape.glyph_to_char.end(), false, chars, glyphs);
  883. }
  884. if (!success) {
  885. std::string glyph_to_char_string;
  886. for (size_t i = 0; i < shape.glyph_count && i < shape.glyph_to_char.size();
  887. ++i) {
  888. glyph_to_char_string += base::NumberToString(i) + "->" +
  889. base::NumberToString(shape.glyph_to_char[i]) +
  890. ", ";
  891. }
  892. LOG(ERROR) << " TextRunHarfBuzz error, please report at crbug.com/724880:"
  893. << " range: " << range.ToString()
  894. << ", rtl: " << font_params.is_rtl << ","
  895. << " level: '" << font_params.level
  896. << "', script: " << font_params.script << ","
  897. << " font: '" << font_params.font.GetActualFontName() << "',"
  898. << " glyph_count: " << shape.glyph_count << ", pos: " << pos
  899. << ","
  900. << " glyph_to_char: " << glyph_to_char_string;
  901. }
  902. }
  903. RangeF TextRunHarfBuzz::GetGraphemeBounds(RenderTextHarfBuzz* render_text,
  904. size_t text_index) const {
  905. DCHECK_LT(text_index, range.end());
  906. if (shape.glyph_count == 0)
  907. return RangeF(preceding_run_widths, preceding_run_widths + shape.width);
  908. Range chars;
  909. Range glyphs;
  910. GetClusterAt(text_index, &chars, &glyphs);
  911. // Obscured glyphs are centered in their allotted space by adjusting their
  912. // positions during shaping. Include the space preceding the glyph when
  913. // calculating grapheme bounds.
  914. const float half_obscured_spacing =
  915. render_text->obscured() ? render_text->obscured_glyph_spacing() / 2.0f
  916. : 0.0f;
  917. const float cluster_begin_x =
  918. shape.positions[glyphs.start()].x() - half_obscured_spacing;
  919. const float cluster_end_x =
  920. glyphs.end() < shape.glyph_count
  921. ? shape.positions[glyphs.end()].x() - half_obscured_spacing
  922. : SkFloatToScalar(shape.width);
  923. DCHECK_LE(cluster_begin_x, cluster_end_x);
  924. // A cluster consists of a number of code points and corresponds to a number
  925. // of glyphs that should be drawn together. A cluster can contain multiple
  926. // graphemes. In order to place the cursor at a grapheme boundary inside the
  927. // cluster, we simply divide the cluster width by the number of graphemes.
  928. ptrdiff_t code_point_count = UTF16IndexToOffset(render_text->GetDisplayText(),
  929. chars.start(), chars.end());
  930. if (code_point_count > 1) {
  931. int before = 0;
  932. int total = 0;
  933. for (size_t i = chars.start(); i < chars.end(); ++i) {
  934. if (render_text->IsGraphemeBoundary(i)) {
  935. if (i < text_index)
  936. ++before;
  937. ++total;
  938. }
  939. }
  940. // With ICU 65.1, DCHECK_GT() below fails.
  941. // See https://crbug.com/1017047 for more details.
  942. //
  943. // DCHECK_GT(total, 0);
  944. // It's possible for |text_index| to point to a diacritical mark, at the end
  945. // of |chars|. In this case all the grapheme boundaries come before it. Just
  946. // provide the bounds of the last grapheme.
  947. if (before == total)
  948. --before;
  949. if (total > 1) {
  950. if (font_params.is_rtl)
  951. before = total - before - 1;
  952. DCHECK_GE(before, 0);
  953. DCHECK_LT(before, total);
  954. const float cluster_start = preceding_run_widths + cluster_begin_x;
  955. const float average_width = (cluster_end_x - cluster_begin_x) / total;
  956. return RangeF(cluster_start + average_width * before,
  957. cluster_start + average_width * (before + 1));
  958. }
  959. }
  960. return RangeF(preceding_run_widths + cluster_begin_x,
  961. preceding_run_widths + cluster_end_x);
  962. }
  963. RangeF TextRunHarfBuzz::GetGraphemeSpanForCharRange(
  964. RenderTextHarfBuzz* render_text,
  965. const Range& char_range) const {
  966. if (char_range.is_empty())
  967. return RangeF();
  968. DCHECK(!char_range.is_reversed());
  969. DCHECK(range.Contains(char_range));
  970. size_t left_index = char_range.start();
  971. size_t right_index =
  972. UTF16OffsetToIndex(render_text->GetDisplayText(), char_range.end(), -1);
  973. DCHECK_LE(left_index, right_index);
  974. if (font_params.is_rtl)
  975. std::swap(left_index, right_index);
  976. const RangeF left_span = GetGraphemeBounds(render_text, left_index);
  977. return left_index == right_index
  978. ? left_span
  979. : RangeF(left_span.start(),
  980. GetGraphemeBounds(render_text, right_index).end());
  981. }
  982. SkScalar TextRunHarfBuzz::GetGlyphWidthForCharRange(
  983. const Range& char_range) const {
  984. if (char_range.is_empty())
  985. return 0;
  986. DCHECK(range.Contains(char_range));
  987. Range glyph_range = CharRangeToGlyphRange(char_range);
  988. // The |glyph_range| might be empty or invalid on Windows if a multi-character
  989. // grapheme is divided into different runs (e.g., there are two font sizes or
  990. // colors for a single glyph). In this case it might cause the browser crash,
  991. // see crbug.com/526234.
  992. if (glyph_range.start() >= glyph_range.end()) {
  993. NOTREACHED() << "The glyph range is empty or invalid! Its char range: ["
  994. << char_range.start() << ", " << char_range.end()
  995. << "], and its glyph range: [" << glyph_range.start() << ", "
  996. << glyph_range.end() << "].";
  997. return 0;
  998. }
  999. return ((glyph_range.end() == shape.glyph_count)
  1000. ? SkFloatToScalar(shape.width)
  1001. : shape.positions[glyph_range.end()].x()) -
  1002. shape.positions[glyph_range.start()].x();
  1003. }
  1004. void TextRunHarfBuzz::UpdateFontParamsAndShape(
  1005. const FontParams& new_font_params,
  1006. const ShapeOutput& new_shape) {
  1007. if (new_shape.missing_glyph_count < shape.missing_glyph_count) {
  1008. font_params = new_font_params;
  1009. shape = new_shape;
  1010. // Note that |new_shape.glyph_to_char| is indexed from the beginning of
  1011. // |range|, while |shape.glyph_to_char| is indexed from the beginning of
  1012. // its embedding text.
  1013. for (size_t i = 0; i < shape.glyph_to_char.size(); ++i)
  1014. shape.glyph_to_char[i] += range.start();
  1015. }
  1016. }
  1017. TextRunList::TextRunList() : width_(0.0f) {}
  1018. TextRunList::~TextRunList() {}
  1019. void TextRunList::Reset() {
  1020. runs_.clear();
  1021. width_ = 0.0f;
  1022. }
  1023. void TextRunList::InitIndexMap() {
  1024. if (runs_.size() == 1) {
  1025. visual_to_logical_ = logical_to_visual_ = std::vector<int32_t>(1, 0);
  1026. return;
  1027. }
  1028. const size_t num_runs = runs_.size();
  1029. std::vector<UBiDiLevel> levels(num_runs);
  1030. for (size_t i = 0; i < num_runs; ++i)
  1031. levels[i] = runs_[i]->font_params.level;
  1032. visual_to_logical_.resize(num_runs);
  1033. ubidi_reorderVisual(&levels[0], num_runs, &visual_to_logical_[0]);
  1034. logical_to_visual_.resize(num_runs);
  1035. ubidi_reorderLogical(&levels[0], num_runs, &logical_to_visual_[0]);
  1036. }
  1037. void TextRunList::ComputePrecedingRunWidths() {
  1038. // Precalculate run width information.
  1039. width_ = 0.0f;
  1040. for (size_t i = 0; i < runs_.size(); ++i) {
  1041. const auto& run = runs_[visual_to_logical_[i]];
  1042. run->preceding_run_widths = width_;
  1043. width_ += run->shape.width;
  1044. }
  1045. }
  1046. size_t TextRunList::GetRunIndexAt(size_t position) const {
  1047. for (size_t i = 0; i < runs_.size(); ++i) {
  1048. if (runs_[i]->range.start() <= position && runs_[i]->range.end() > position)
  1049. return i;
  1050. }
  1051. return runs_.size();
  1052. }
  1053. namespace {
  1054. // ShapeRunWithFont cache. Views makes repeated calls to ShapeRunWithFont
  1055. // with the same arguments in several places, and typesetting is very expensive.
  1056. // To compensate for this, encapsulate all of the input arguments to
  1057. // ShapeRunWithFont in ShapeRunWithFontInput, all of the output arguments in
  1058. // TextRunHarfBuzz::ShapeOutput, and add ShapeRunCache to map between the two.
  1059. // This is analogous to the blink::ShapeCache.
  1060. // https://crbug.com/826265
  1061. // Input for the stateless implementation of ShapeRunWithFont.
  1062. struct ShapeRunWithFontInput {
  1063. ShapeRunWithFontInput(const std::u16string& full_text,
  1064. const TextRunHarfBuzz::FontParams& font_params,
  1065. Range full_range,
  1066. bool obscured,
  1067. float glyph_width_for_test,
  1068. int obscured_glyph_spacing,
  1069. bool subpixel_rendering_suppressed)
  1070. : skia_face(font_params.skia_face),
  1071. render_params(font_params.render_params),
  1072. script(font_params.script),
  1073. font_size(font_params.font_size),
  1074. obscured_glyph_spacing(obscured_glyph_spacing),
  1075. glyph_width_for_test(glyph_width_for_test),
  1076. is_rtl(font_params.is_rtl),
  1077. obscured(obscured),
  1078. subpixel_rendering_suppressed(subpixel_rendering_suppressed) {
  1079. // hb_buffer_add_utf16 will read the previous and next 5 unicode characters
  1080. // (which can have a maximum length of 2 uint16_t) as "context" that is used
  1081. // only for Arabic (which is RTL). Read the previous and next 10 uint16_ts
  1082. // to ensure that we capture all of this context if we're using RTL.
  1083. size_t kContextSize = is_rtl ? 10 : 0;
  1084. size_t context_start = full_range.start() < kContextSize
  1085. ? 0
  1086. : full_range.start() - kContextSize;
  1087. size_t context_end =
  1088. std::min(full_text.length(), full_range.end() + kContextSize);
  1089. range = Range(full_range.start() - context_start,
  1090. full_range.end() - context_start);
  1091. text = full_text.substr(context_start, context_end - context_start);
  1092. // Pre-compute the hash to avoid having to re-hash at every comparison.
  1093. // Attempt to minimize collisions by including the typeface, script, font
  1094. // size, text and the text range.
  1095. hash = base::HashInts(hash, skia_face->uniqueID());
  1096. hash = base::HashInts(hash, script);
  1097. hash = base::HashInts(hash, font_size);
  1098. hash = base::Hash(text);
  1099. hash = base::HashInts(hash, range.start());
  1100. hash = base::HashInts(hash, range.length());
  1101. }
  1102. bool operator==(const ShapeRunWithFontInput& other) const {
  1103. return text == other.text && skia_face == other.skia_face &&
  1104. render_params == other.render_params &&
  1105. font_size == other.font_size && range == other.range &&
  1106. script == other.script && is_rtl == other.is_rtl &&
  1107. obscured == other.obscured &&
  1108. glyph_width_for_test == other.glyph_width_for_test &&
  1109. obscured_glyph_spacing == other.obscured_glyph_spacing &&
  1110. subpixel_rendering_suppressed == other.subpixel_rendering_suppressed;
  1111. }
  1112. struct Hash {
  1113. size_t operator()(const ShapeRunWithFontInput& key) const {
  1114. return key.hash;
  1115. }
  1116. };
  1117. sk_sp<SkTypeface> skia_face;
  1118. FontRenderParams render_params;
  1119. UScriptCode script;
  1120. int font_size;
  1121. int obscured_glyph_spacing;
  1122. float glyph_width_for_test;
  1123. bool is_rtl;
  1124. bool obscured;
  1125. bool subpixel_rendering_suppressed;
  1126. // The parts of the input text that may be read by hb_buffer_add_utf16.
  1127. std::u16string text;
  1128. // The conversion of the input range to a range within |text|.
  1129. Range range;
  1130. // The hash is cached to avoid repeated calls.
  1131. size_t hash = 0;
  1132. };
  1133. // An LRU cache of the results from calling ShapeRunWithFont. The maximum cache
  1134. // size used in blink::ShapeCache is 10k. A Finch experiment showed that
  1135. // reducing the cache size to 1k has no performance impact.
  1136. constexpr int kShapeRunCacheSize = 1000;
  1137. using ShapeRunCacheBase = base::HashingLRUCache<ShapeRunWithFontInput,
  1138. TextRunHarfBuzz::ShapeOutput,
  1139. ShapeRunWithFontInput::Hash>;
  1140. class ShapeRunCache : public ShapeRunCacheBase {
  1141. public:
  1142. ShapeRunCache() : ShapeRunCacheBase(kShapeRunCacheSize) {}
  1143. };
  1144. void ShapeRunWithFont(const ShapeRunWithFontInput& in,
  1145. TextRunHarfBuzz::ShapeOutput* out) {
  1146. TRACE_EVENT0("ui", "RenderTextHarfBuzz::ShapeRunWithFontInternal");
  1147. hb_font_t* harfbuzz_font =
  1148. CreateHarfBuzzFont(in.skia_face, SkIntToScalar(in.font_size),
  1149. in.render_params, in.subpixel_rendering_suppressed);
  1150. // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz
  1151. // buffer holds our text, run information to be used by the shaping engine,
  1152. // and the resulting glyph data.
  1153. hb_buffer_t* buffer = hb_buffer_create();
  1154. // Note that the value of the |item_offset| argument (here specified as
  1155. // |in.range.start()|) does affect the result, so we will have to adjust
  1156. // the computed offsets.
  1157. hb_buffer_add_utf16(
  1158. buffer, reinterpret_cast<const uint16_t*>(in.text.c_str()),
  1159. static_cast<int>(in.text.length()), in.range.start(), in.range.length());
  1160. hb_buffer_set_script(buffer, ICUScriptToHBScript(in.script));
  1161. hb_buffer_set_direction(buffer,
  1162. in.is_rtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
  1163. // TODO(ckocagil): Should we determine the actual language?
  1164. hb_buffer_set_language(buffer, hb_language_get_default());
  1165. // Shape the text.
  1166. hb_shape(harfbuzz_font, buffer, NULL, 0);
  1167. // Populate the run fields with the resulting glyph data in the buffer.
  1168. unsigned int glyph_count = 0;
  1169. hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count);
  1170. out->glyph_count = glyph_count;
  1171. hb_glyph_position_t* hb_positions =
  1172. hb_buffer_get_glyph_positions(buffer, NULL);
  1173. out->glyphs.resize(out->glyph_count);
  1174. out->glyph_to_char.resize(out->glyph_count);
  1175. out->positions.resize(out->glyph_count);
  1176. out->width = 0.0f;
  1177. // Font on MAC like ".SF NS Text" may have a negative x_offset. Positive
  1178. // x_offset are also found on Windows (e.g. "Segoe UI"). It requires tests
  1179. // relying on the behavior of |glyph_width_for_test_| to also be given a zero
  1180. // x_offset, otherwise expectations get thrown off
  1181. // (see: http://crbug.com/1056220).
  1182. const bool force_zero_offset = in.glyph_width_for_test > 0;
  1183. constexpr uint16_t kMissingGlyphId = 0;
  1184. out->missing_glyph_count = 0;
  1185. for (size_t i = 0; i < out->glyph_count; ++i) {
  1186. DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16_t>::max());
  1187. uint16_t glyph = static_cast<uint16_t>(infos[i].codepoint);
  1188. out->glyphs[i] = glyph;
  1189. if (glyph == kMissingGlyphId)
  1190. out->missing_glyph_count += 1;
  1191. DCHECK_GE(infos[i].cluster, in.range.start());
  1192. out->glyph_to_char[i] = infos[i].cluster - in.range.start();
  1193. SkScalar x_offset = HarfBuzzUnitsToSkiaScalar(hb_positions[i].x_offset);
  1194. if (in.obscured)
  1195. // Place obscured glyphs in the middle of the allotted spacing.
  1196. x_offset += in.obscured_glyph_spacing / 2.0f;
  1197. if (force_zero_offset)
  1198. x_offset = 0;
  1199. const SkScalar y_offset =
  1200. HarfBuzzUnitsToSkiaScalar(hb_positions[i].y_offset);
  1201. out->positions[i].set(out->width + x_offset, -y_offset);
  1202. if (in.glyph_width_for_test == 0)
  1203. out->width += HarfBuzzUnitsToFloat(hb_positions[i].x_advance);
  1204. else if (hb_positions[i].x_advance) // Leave zero-width glyphs alone.
  1205. out->width += in.glyph_width_for_test;
  1206. if (in.obscured)
  1207. out->width += in.obscured_glyph_spacing;
  1208. // When subpixel positioning is not enabled, glyph width is rounded to avoid
  1209. // fractional width. Disable this conversion when a glyph width is provided
  1210. // for testing. Using an integral glyph width has the same behavior as
  1211. // disabling the subpixel positioning.
  1212. const bool force_subpixel_for_test = in.glyph_width_for_test != 0;
  1213. // Round run widths if subpixel positioning is off to match native behavior.
  1214. if (!in.render_params.subpixel_positioning && !force_subpixel_for_test)
  1215. out->width = std::round(out->width);
  1216. }
  1217. hb_buffer_destroy(buffer);
  1218. hb_font_destroy(harfbuzz_font);
  1219. }
  1220. std::string GetApplicationLocale() {
  1221. #if BUILDFLAG(IS_ANDROID)
  1222. // TODO(etienneb): Android locale should work the same way than base locale.
  1223. return base::android::GetDefaultLocaleString();
  1224. #else
  1225. return base::i18n::GetConfiguredLocale();
  1226. #endif
  1227. }
  1228. } // namespace
  1229. } // namespace internal
  1230. RenderTextHarfBuzz::RenderTextHarfBuzz()
  1231. : RenderText(),
  1232. update_layout_run_list_(false),
  1233. update_display_run_list_(false),
  1234. update_display_text_(false),
  1235. locale_(internal::GetApplicationLocale()) {
  1236. set_truncate_length(kMaxTextLength);
  1237. }
  1238. RenderTextHarfBuzz::~RenderTextHarfBuzz() {}
  1239. const std::u16string& RenderTextHarfBuzz::GetDisplayText() {
  1240. // TODO(krb): Consider other elision modes for multiline.
  1241. if ((multiline() && (max_lines() == 0 || elide_behavior() != ELIDE_TAIL)) ||
  1242. elide_behavior() == NO_ELIDE || elide_behavior() == FADE_TAIL) {
  1243. // Call UpdateDisplayText to clear |display_text_| and |text_elided_|
  1244. // on the RenderText class.
  1245. UpdateDisplayText(0);
  1246. update_display_text_ = false;
  1247. display_run_list_.reset();
  1248. return GetLayoutText();
  1249. }
  1250. EnsureLayoutRunList();
  1251. DCHECK(!update_display_text_);
  1252. return text_elided() ? display_text() : GetLayoutText();
  1253. }
  1254. SizeF RenderTextHarfBuzz::GetStringSizeF() {
  1255. EnsureLayout();
  1256. return total_size_;
  1257. }
  1258. SizeF RenderTextHarfBuzz::GetLineSizeF(const SelectionModel& caret) {
  1259. const internal::ShapedText* shaped_text = GetShapedText();
  1260. const auto& caret_run = GetRunContainingCaret(caret);
  1261. for (const auto& line : shaped_text->lines()) {
  1262. for (const internal::LineSegment& segment : line.segments) {
  1263. if (segment.run == caret_run)
  1264. return line.size;
  1265. }
  1266. }
  1267. return shaped_text->lines().back().size;
  1268. }
  1269. std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) {
  1270. EnsureLayout();
  1271. DCHECK(!update_display_run_list_);
  1272. DCHECK(range.IsBoundedBy(Range(0, text().length())));
  1273. const Range grapheme_range = ExpandRangeToGraphemeBoundary(range);
  1274. const Range display_range(TextIndexToDisplayIndex(grapheme_range.start()),
  1275. TextIndexToDisplayIndex(grapheme_range.end()));
  1276. DCHECK(IsValidDisplayRange(display_range));
  1277. std::vector<Rect> rects;
  1278. if (display_range.is_empty())
  1279. return rects;
  1280. internal::TextRunList* run_list = GetRunList();
  1281. const internal::ShapedText* shaped_text = GetShapedText();
  1282. for (size_t line_index = 0; line_index < shaped_text->lines().size();
  1283. ++line_index) {
  1284. const internal::Line& line = shaped_text->lines()[line_index];
  1285. // Only the last line can be empty.
  1286. DCHECK(!line.segments.empty() ||
  1287. (line_index == shaped_text->lines().size() - 1));
  1288. float line_start_x =
  1289. line.segments.empty()
  1290. ? 0
  1291. : run_list->runs()[line.segments[0].run]->preceding_run_widths;
  1292. if (line.segments.size() > 1 && IsNewlineSegment(line.segments[0]))
  1293. line_start_x += line.segments[0].width();
  1294. std::vector<Rect> current_line_rects;
  1295. for (const internal::LineSegment& segment : line.segments) {
  1296. const Range intersection = segment.char_range.Intersect(display_range);
  1297. DCHECK(!intersection.is_reversed());
  1298. if (!intersection.is_empty()) {
  1299. const internal::TextRunHarfBuzz& run = *run_list->runs()[segment.run];
  1300. RangeF selected_span =
  1301. run.GetGraphemeSpanForCharRange(this, intersection);
  1302. DCHECK(!selected_span.is_reversed());
  1303. int start_x = base::ClampFloor(selected_span.start() - line_start_x);
  1304. int end_x = base::ClampCeil(selected_span.end() - line_start_x);
  1305. Rect rect(start_x, 0, end_x - start_x,
  1306. base::ClampCeil(line.size.height()));
  1307. current_line_rects.push_back(rect + GetLineOffset(line_index));
  1308. }
  1309. }
  1310. MergeIntersectingRects(current_line_rects);
  1311. rects.insert(rects.end(), current_line_rects.begin(),
  1312. current_line_rects.end());
  1313. }
  1314. return rects;
  1315. }
  1316. RangeF RenderTextHarfBuzz::GetCursorSpan(const Range& text_range) {
  1317. DCHECK(!text_range.is_reversed());
  1318. EnsureLayout();
  1319. const size_t index = text_range.start();
  1320. size_t run_index =
  1321. GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
  1322. internal::TextRunList* run_list = GetRunList();
  1323. // Return zero if the text is empty.
  1324. if (run_list->size() == 0 || text().empty())
  1325. return RangeF(0);
  1326. // Use the last run if the index is invalid or beyond the layout text size.
  1327. Range valid_range(text_range.start(), text_range.end());
  1328. if (run_index >= run_list->size()) {
  1329. valid_range = Range(text().length() - 1, text().length());
  1330. run_index = run_list->size() - 1;
  1331. }
  1332. internal::TextRunHarfBuzz* run = run_list->runs()[run_index].get();
  1333. size_t next_grapheme_start = valid_range.end();
  1334. if (!IsValidCursorIndex(next_grapheme_start)) {
  1335. next_grapheme_start =
  1336. IndexOfAdjacentGrapheme(next_grapheme_start, CURSOR_FORWARD);
  1337. }
  1338. Range display_range(TextIndexToDisplayIndex(valid_range.start()),
  1339. TextIndexToDisplayIndex(next_grapheme_start));
  1340. DCHECK(IsValidDisplayRange(display_range));
  1341. // Although highly likely, there's no guarantee that a single text run is used
  1342. // for the entire cursor span. For example, Unicode Variation Selectors are
  1343. // incorrectly placed in the next run; see crbug.com/775404. (For these, the
  1344. // variation selector has zero width, so it's safe to ignore the second run).
  1345. // TODO(tapted): Change this to a DCHECK when crbug.com/775404 is fixed.
  1346. display_range = display_range.Intersect(run->range);
  1347. RangeF bounds = run->GetGraphemeSpanForCharRange(this, display_range);
  1348. return run->font_params.is_rtl ? RangeF(bounds.end(), bounds.start())
  1349. : bounds;
  1350. }
  1351. size_t RenderTextHarfBuzz::GetLineContainingCaret(const SelectionModel& caret) {
  1352. EnsureLayout();
  1353. if (caret.caret_pos() == 0)
  1354. return 0;
  1355. if (!multiline()) {
  1356. DCHECK_EQ(1u, GetShapedText()->lines().size());
  1357. return 0;
  1358. }
  1359. size_t layout_position = TextIndexToDisplayIndex(caret.caret_pos());
  1360. LogicalCursorDirection affinity = caret.caret_affinity();
  1361. const internal::ShapedText* shaped_text = GetShapedText();
  1362. for (size_t line_index = 0; line_index < shaped_text->lines().size();
  1363. ++line_index) {
  1364. const internal::Line& line = shaped_text->lines()[line_index];
  1365. for (const internal::LineSegment& segment : line.segments) {
  1366. if (RangeContainsCaret(segment.char_range, layout_position, affinity))
  1367. return LineIndexForNewline(line_index, text(), segment, caret);
  1368. }
  1369. }
  1370. return shaped_text->lines().size() - 1;
  1371. }
  1372. SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel(
  1373. const SelectionModel& selection,
  1374. VisualCursorDirection direction) {
  1375. DCHECK(!update_display_run_list_);
  1376. internal::TextRunList* run_list = GetRunList();
  1377. internal::TextRunHarfBuzz* run;
  1378. size_t run_index = GetRunContainingCaret(selection);
  1379. if (run_index >= run_list->size()) {
  1380. // The cursor is not in any run: we're at the visual and logical edge.
  1381. SelectionModel edge = EdgeSelectionModel(direction);
  1382. if (edge.caret_pos() == selection.caret_pos())
  1383. return edge;
  1384. int visual_index = (direction == CURSOR_RIGHT) ? 0 : run_list->size() - 1;
  1385. run = run_list->runs()[run_list->visual_to_logical(visual_index)].get();
  1386. } else {
  1387. // If the cursor is moving within the current run, just move it by one
  1388. // grapheme in the appropriate direction.
  1389. run = run_list->runs()[run_index].get();
  1390. size_t caret = selection.caret_pos();
  1391. bool forward_motion = run->font_params.is_rtl == (direction == CURSOR_LEFT);
  1392. if (forward_motion) {
  1393. if (caret < DisplayIndexToTextIndex(run->range.end())) {
  1394. caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
  1395. return SelectionModel(caret, CURSOR_BACKWARD);
  1396. }
  1397. } else {
  1398. if (caret > DisplayIndexToTextIndex(run->range.start())) {
  1399. caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
  1400. return SelectionModel(caret, CURSOR_FORWARD);
  1401. }
  1402. }
  1403. // The cursor is at the edge of a run; move to the visually adjacent run.
  1404. int visual_index = run_list->logical_to_visual(run_index);
  1405. visual_index += (direction == CURSOR_LEFT) ? -1 : 1;
  1406. if (visual_index < 0 || visual_index >= static_cast<int>(run_list->size()))
  1407. return EdgeSelectionModel(direction);
  1408. run = run_list->runs()[run_list->visual_to_logical(visual_index)].get();
  1409. }
  1410. bool forward_motion = run->font_params.is_rtl == (direction == CURSOR_LEFT);
  1411. return forward_motion ? FirstSelectionModelInsideRun(run) :
  1412. LastSelectionModelInsideRun(run);
  1413. }
  1414. SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel(
  1415. const SelectionModel& selection,
  1416. VisualCursorDirection direction) {
  1417. if (obscured())
  1418. return EdgeSelectionModel(direction);
  1419. base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
  1420. bool success = iter.Init();
  1421. DCHECK(success);
  1422. if (!success)
  1423. return selection;
  1424. internal::TextRunList* run_list = GetRunList();
  1425. SelectionModel current(selection);
  1426. for (;;) {
  1427. current = AdjacentCharSelectionModel(current, direction);
  1428. size_t run = GetRunContainingCaret(current);
  1429. if (run == run_list->size())
  1430. break;
  1431. size_t cursor = current.caret_pos();
  1432. #if BUILDFLAG(IS_WIN)
  1433. // Windows generally advances to the start of a word in either direction.
  1434. // TODO: Break on the end of a word when the neighboring text is
  1435. // punctuation.
  1436. if (iter.IsStartOfWord(cursor))
  1437. break;
  1438. #else
  1439. const bool is_forward =
  1440. run_list->runs()[run]->font_params.is_rtl == (direction == CURSOR_LEFT);
  1441. if (is_forward ? iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor))
  1442. break;
  1443. #endif // BUILDFLAG(IS_WIN)
  1444. }
  1445. return current;
  1446. }
  1447. SelectionModel RenderTextHarfBuzz::AdjacentLineSelectionModel(
  1448. const SelectionModel& selection,
  1449. VisualCursorDirection direction) {
  1450. DCHECK(direction == CURSOR_UP || direction == CURSOR_DOWN);
  1451. size_t line = GetLineContainingCaret(selection);
  1452. if (line == 0 && direction == CURSOR_UP) {
  1453. reset_cached_cursor_x();
  1454. return SelectionModel(0, CURSOR_BACKWARD);
  1455. }
  1456. if (line == GetShapedText()->lines().size() - 1 && direction == CURSOR_DOWN) {
  1457. reset_cached_cursor_x();
  1458. return SelectionModel(text().length(), CURSOR_FORWARD);
  1459. }
  1460. direction == CURSOR_UP ? --line : ++line;
  1461. Rect bounds = GetCursorBounds(selection, true);
  1462. Point target = bounds.origin();
  1463. if (cached_cursor_x())
  1464. target.set_x(cached_cursor_x().value());
  1465. else
  1466. set_cached_cursor_x(target.x());
  1467. if (direction == CURSOR_UP)
  1468. target.Offset(0, -bounds.size().height() / 2);
  1469. else
  1470. target.Offset(0, bounds.size().height() * 3 / 2);
  1471. SelectionModel next = FindCursorPosition(target, Point());
  1472. size_t next_line = GetLineContainingCaret(next);
  1473. // If the |target| position is at the newline character, the caret is drawn to
  1474. // the next line. e.g., when the caret is at the beginning of the line in RTL
  1475. // text. Move the caret to the position of the previous character to move the
  1476. // caret to the previous line.
  1477. if (next_line == line + 1)
  1478. next = SelectionModel(next.caret_pos() - 1, next.caret_affinity());
  1479. return next;
  1480. }
  1481. void RenderTextHarfBuzz::OnLayoutTextAttributeChanged(bool text_changed) {
  1482. RenderText::OnLayoutTextAttributeChanged(text_changed);
  1483. update_layout_run_list_ = true;
  1484. OnDisplayTextAttributeChanged();
  1485. }
  1486. void RenderTextHarfBuzz::OnDisplayTextAttributeChanged() {
  1487. update_display_text_ = true;
  1488. set_shaped_text(nullptr);
  1489. }
  1490. void RenderTextHarfBuzz::EnsureLayout() {
  1491. EnsureLayoutRunList();
  1492. if (update_display_run_list_) {
  1493. DCHECK(text_elided());
  1494. const std::u16string& display_text = GetDisplayText();
  1495. display_run_list_ = std::make_unique<internal::TextRunList>();
  1496. if (!display_text.empty())
  1497. ItemizeAndShapeText(display_text, display_run_list_.get());
  1498. update_display_run_list_ = false;
  1499. set_shaped_text(nullptr);
  1500. }
  1501. if (!has_shaped_text()) {
  1502. internal::TextRunList* run_list = GetRunList();
  1503. const int height = std::max(font_list().GetHeight(), min_line_height());
  1504. HarfBuzzLineBreaker line_breaker(
  1505. display_rect().width(),
  1506. DetermineBaselineCenteringText(height, font_list()), height,
  1507. glyph_height_for_test_, word_wrap_behavior(), GetDisplayText(),
  1508. *run_list);
  1509. if (multiline())
  1510. line_breaker.ConstructMultiLines();
  1511. else
  1512. line_breaker.ConstructSingleLine();
  1513. std::vector<internal::Line> lines;
  1514. line_breaker.FinalizeLines(&lines, &total_size_);
  1515. // In multiline, only ELIDE_TAIL is supported. max_lines_ is not used
  1516. // otherwise.
  1517. if (multiline() && max_lines() && elide_behavior() == ELIDE_TAIL) {
  1518. // TODO(crbug.com/866720): no more than max_lines() should be rendered.
  1519. // Remove the IsHomogeneous() condition for the following DCHECK when the
  1520. // bug is fixed.
  1521. if (IsHomogeneous()) {
  1522. DCHECK_LE(lines.size(), max_lines());
  1523. }
  1524. }
  1525. set_shaped_text(std::make_unique<internal::ShapedText>(lines));
  1526. }
  1527. }
  1528. void RenderTextHarfBuzz::DrawVisualText(internal::SkiaTextRenderer* renderer,
  1529. const std::vector<Range>& selections) {
  1530. DCHECK(!update_layout_run_list_);
  1531. DCHECK(!update_display_run_list_);
  1532. DCHECK(!update_display_text_);
  1533. const internal::ShapedText* shaped_text = GetShapedText();
  1534. if (shaped_text->lines().empty())
  1535. return;
  1536. ApplyFadeEffects(renderer);
  1537. ApplyTextShadows(renderer);
  1538. // Apply the selected text color to the [un-reversed] selection range.
  1539. BreakList<SkColor> colors = layout_colors();
  1540. for (auto selection : selections) {
  1541. if (!selection.is_empty()) {
  1542. const Range grapheme_range = ExpandRangeToGraphemeBoundary(selection);
  1543. colors.ApplyValue(
  1544. selection_color(),
  1545. Range(TextIndexToDisplayIndex(grapheme_range.GetMin()),
  1546. TextIndexToDisplayIndex(grapheme_range.GetMax())));
  1547. }
  1548. }
  1549. internal::TextRunList* run_list = GetRunList();
  1550. const std::u16string& display_text = GetDisplayText();
  1551. for (size_t i = 0; i < shaped_text->lines().size(); ++i) {
  1552. const internal::Line& line = shaped_text->lines()[i];
  1553. const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline);
  1554. SkScalar preceding_segment_widths = 0;
  1555. for (const internal::LineSegment& segment : line.segments) {
  1556. // Don't draw the newline glyph (crbug.com/680430).
  1557. if (IsNewlineSegment(display_text, segment))
  1558. continue;
  1559. const internal::TextRunHarfBuzz& run = *run_list->runs()[segment.run];
  1560. renderer->SetTypeface(run.font_params.skia_face);
  1561. renderer->SetTextSize(SkIntToScalar(run.font_params.font_size));
  1562. renderer->SetFontRenderParams(run.font_params.render_params,
  1563. subpixel_rendering_suppressed());
  1564. Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range);
  1565. std::vector<SkPoint> positions(glyphs_range.length());
  1566. SkScalar offset_x = preceding_segment_widths -
  1567. ((glyphs_range.GetMin() != 0)
  1568. ? run.shape.positions[glyphs_range.GetMin()].x()
  1569. : 0);
  1570. for (size_t j = 0; j < glyphs_range.length(); ++j) {
  1571. positions[j] = run.shape.positions[(glyphs_range.is_reversed())
  1572. ? (glyphs_range.start() - j)
  1573. : (glyphs_range.start() + j)];
  1574. positions[j].offset(
  1575. SkIntToScalar(origin.x()) + offset_x,
  1576. SkIntToScalar(origin.y() + run.font_params.baseline_offset));
  1577. }
  1578. for (auto it = colors.GetBreak(segment.char_range.start());
  1579. it != colors.breaks().end() && it->first < segment.char_range.end();
  1580. ++it) {
  1581. const Range intersection =
  1582. colors.GetRange(it).Intersect(segment.char_range);
  1583. const Range colored_glyphs = run.CharRangeToGlyphRange(intersection);
  1584. // The range may be empty if a portion of a multi-character grapheme is
  1585. // selected, yielding two colors for a single glyph. For now, this just
  1586. // paints the glyph with a single style, but it should paint it twice,
  1587. // clipped according to selection bounds. See http://crbug.com/366786
  1588. if (colored_glyphs.is_empty())
  1589. continue;
  1590. renderer->SetForegroundColor(it->second);
  1591. renderer->DrawPosText(
  1592. &positions[colored_glyphs.start() - glyphs_range.start()],
  1593. &run.shape.glyphs[colored_glyphs.start()], colored_glyphs.length());
  1594. int start_x = SkScalarRoundToInt(
  1595. positions[colored_glyphs.start() - glyphs_range.start()].x());
  1596. int end_x = SkScalarRoundToInt(
  1597. (colored_glyphs.end() == glyphs_range.end())
  1598. ? (SkFloatToScalar(segment.width()) + preceding_segment_widths +
  1599. SkIntToScalar(origin.x()))
  1600. : positions[colored_glyphs.end() - glyphs_range.start()].x());
  1601. if (run.font_params.heavy_underline)
  1602. renderer->DrawUnderline(start_x, origin.y(), end_x - start_x, 2.0);
  1603. else if (run.font_params.underline)
  1604. renderer->DrawUnderline(start_x, origin.y(), end_x - start_x);
  1605. if (run.font_params.strike)
  1606. renderer->DrawStrike(start_x, origin.y(), end_x - start_x,
  1607. strike_thickness_factor());
  1608. }
  1609. preceding_segment_widths += SkFloatToScalar(segment.width());
  1610. }
  1611. }
  1612. }
  1613. size_t RenderTextHarfBuzz::GetRunContainingCaret(
  1614. const SelectionModel& caret) {
  1615. DCHECK(!update_display_run_list_);
  1616. size_t layout_position = TextIndexToDisplayIndex(caret.caret_pos());
  1617. LogicalCursorDirection affinity = caret.caret_affinity();
  1618. internal::TextRunList* run_list = GetRunList();
  1619. for (size_t i = 0; i < run_list->size(); ++i) {
  1620. internal::TextRunHarfBuzz* run = run_list->runs()[i].get();
  1621. if (RangeContainsCaret(run->range, layout_position, affinity))
  1622. return i;
  1623. }
  1624. return run_list->size();
  1625. }
  1626. SelectionModel RenderTextHarfBuzz::FirstSelectionModelInsideRun(
  1627. const internal::TextRunHarfBuzz* run) {
  1628. size_t position = DisplayIndexToTextIndex(run->range.start());
  1629. position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD);
  1630. return SelectionModel(position, CURSOR_BACKWARD);
  1631. }
  1632. SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun(
  1633. const internal::TextRunHarfBuzz* run) {
  1634. size_t position = DisplayIndexToTextIndex(run->range.end());
  1635. position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
  1636. return SelectionModel(position, CURSOR_FORWARD);
  1637. }
  1638. void RenderTextHarfBuzz::ItemizeAndShapeText(const std::u16string& text,
  1639. internal::TextRunList* run_list) {
  1640. CommonizedRunsMap commonized_run_map;
  1641. ItemizeTextToRuns(text, run_list, &commonized_run_map);
  1642. for (auto iter = commonized_run_map.begin(); iter != commonized_run_map.end();
  1643. ++iter) {
  1644. internal::TextRunHarfBuzz::FontParams font_params = iter->first;
  1645. font_params.ComputeRenderParamsFontSizeAndBaselineOffset();
  1646. ShapeRuns(text, font_params, std::move(iter->second));
  1647. }
  1648. run_list->InitIndexMap();
  1649. run_list->ComputePrecedingRunWidths();
  1650. }
  1651. void RenderTextHarfBuzz::ItemizeTextToRuns(
  1652. const std::u16string& text,
  1653. internal::TextRunList* out_run_list,
  1654. CommonizedRunsMap* out_commonized_run_map) {
  1655. TRACE_EVENT1("ui", "RenderTextHarfBuzz::ItemizeTextToRuns", "text_length",
  1656. text.length());
  1657. DCHECK(!text.empty());
  1658. const Font& primary_font = font_list().GetPrimaryFont();
  1659. // If ICU fails to itemize the text, we create a run that spans the entire
  1660. // text. This is needed because leaving the runs set empty causes some clients
  1661. // to misbehave since they expect non-zero text metrics from a non-empty text.
  1662. ui::gfx::BiDiLineIterator bidi_iterator;
  1663. if (!bidi_iterator.Open(text, GetTextDirectionForGivenText(text))) {
  1664. auto run = std::make_unique<internal::TextRunHarfBuzz>(
  1665. font_list().GetPrimaryFont());
  1666. run->range = Range(0, text.length());
  1667. internal::TextRunHarfBuzz::FontParams font_params(primary_font);
  1668. (*out_commonized_run_map)[font_params].push_back(run.get());
  1669. out_run_list->Add(std::move(run));
  1670. return;
  1671. }
  1672. // Iterator to split ranged styles and baselines. The color attributes don't
  1673. // break text runs to keep ligature between graphemes (e.g. Arabic word).
  1674. internal::StyleIterator style = GetLayoutTextStyleIterator();
  1675. // Split the original text by logical runs, then each logical run by common
  1676. // script and each sequence at special characters and style boundaries. This
  1677. // invariant holds: bidi_run_start <= script_run_start <= breaking_run_start
  1678. // <= breaking_run_end <= script_run_end <= bidi_run_end
  1679. for (size_t bidi_run_start = 0; bidi_run_start < text.length();) {
  1680. // Determine the longest logical run (e.g. same bidi direction) from this
  1681. // point.
  1682. int32_t bidi_run_break = 0;
  1683. UBiDiLevel bidi_level = 0;
  1684. bidi_iterator.GetLogicalRun(bidi_run_start, &bidi_run_break, &bidi_level);
  1685. size_t bidi_run_end = static_cast<size_t>(bidi_run_break);
  1686. DCHECK_LT(bidi_run_start, bidi_run_end);
  1687. ApplyForcedDirection(&bidi_level);
  1688. for (size_t script_run_start = bidi_run_start;
  1689. script_run_start < bidi_run_end;) {
  1690. // Find the longest sequence of characters that have at least one common
  1691. // UScriptCode value.
  1692. UScriptCode script = USCRIPT_INVALID_CODE;
  1693. size_t script_run_end =
  1694. ScriptInterval(text, script_run_start,
  1695. bidi_run_end - script_run_start, &script) +
  1696. script_run_start;
  1697. DCHECK_LT(script_run_start, script_run_end);
  1698. for (size_t breaking_run_start = script_run_start;
  1699. breaking_run_start < script_run_end;) {
  1700. // Find the break boundary for style. The style won't break a grapheme
  1701. // since the style of the first character is applied to the whole
  1702. // grapheme.
  1703. style.IncrementToPosition(breaking_run_start);
  1704. size_t text_style_end = style.GetTextBreakingRange().end();
  1705. // Break runs at certain characters that need to be rendered separately
  1706. // to prevent an unusual character from forcing a fallback font on the
  1707. // entire run. After script intersection, many codepoints end up in the
  1708. // script COMMON but can't be rendered together.
  1709. size_t breaking_run_end = FindRunBreakingCharacter(
  1710. text, script, breaking_run_start, text_style_end, script_run_end);
  1711. DCHECK_LT(breaking_run_start, breaking_run_end);
  1712. DCHECK(IsValidCodePointIndex(text, breaking_run_end));
  1713. // Set the font params for the current run for the current run break.
  1714. internal::TextRunHarfBuzz::FontParams font_params =
  1715. CreateFontParams(primary_font, bidi_level, script, style);
  1716. // Create the current run from [breaking_run_start, breaking_run_end[.
  1717. auto run = std::make_unique<internal::TextRunHarfBuzz>(primary_font);
  1718. run->range = Range(breaking_run_start, breaking_run_end);
  1719. // Add the created run to the set of runs.
  1720. (*out_commonized_run_map)[font_params].push_back(run.get());
  1721. out_run_list->Add(std::move(run));
  1722. // Move to the next run.
  1723. breaking_run_start = breaking_run_end;
  1724. }
  1725. // Move to the next script sequence.
  1726. script_run_start = script_run_end;
  1727. }
  1728. // Move to the next direction sequence.
  1729. bidi_run_start = bidi_run_end;
  1730. }
  1731. }
  1732. void RenderTextHarfBuzz::ShapeRuns(
  1733. const std::u16string& text,
  1734. const internal::TextRunHarfBuzz::FontParams& font_params,
  1735. std::vector<internal::TextRunHarfBuzz*> runs) {
  1736. TRACE_EVENT1("ui", "RenderTextHarfBuzz::ShapeRuns", "run_count", runs.size());
  1737. // Runs with a single newline character should be skipped since they can't be
  1738. // rendered (see http://crbug/680430). The following code sets the runs
  1739. // shaping output to report report the missing glyph and removes the runs from
  1740. // the vector of runs to shape. The newline character doesn't have a
  1741. // glyph, which otherwise forces this function to go through the expensive
  1742. // font fallbacks before reporting a missing glyph (see http://crbug/972090).
  1743. std::vector<internal::TextRunHarfBuzz*> need_shaping_runs;
  1744. for (internal::TextRunHarfBuzz*& run : runs) {
  1745. if ((run->range.length() == 1 && (text[run->range.start()] == '\r' ||
  1746. text[run->range.start()] == '\n')) ||
  1747. (run->range.length() == 2 && text[run->range.start()] == '\r' &&
  1748. text[run->range.start() + 1] == '\n')) {
  1749. // Newline runs can't be shaped. Shape this run as if the glyph is
  1750. // missing.
  1751. run->font_params = font_params;
  1752. run->shape.missing_glyph_count = 1;
  1753. run->shape.glyph_count = 1;
  1754. run->shape.glyphs.resize(run->shape.glyph_count);
  1755. run->shape.glyph_to_char.resize(run->shape.glyph_count);
  1756. run->shape.positions.resize(run->shape.glyph_count);
  1757. // Keep width as zero since newline character doesn't have a width.
  1758. } else {
  1759. // This run needs shaping.
  1760. need_shaping_runs.push_back(run);
  1761. }
  1762. }
  1763. runs.swap(need_shaping_runs);
  1764. if (runs.empty()) {
  1765. RecordShapeRunsFallback(ShapeRunFallback::NO_FALLBACK);
  1766. return;
  1767. }
  1768. // Keep a set of fonts already tried for shaping runs.
  1769. std::set<SkFontID> fallback_fonts_already_tried;
  1770. std::vector<Font> fallback_font_candidates;
  1771. // Shaping with primary configured fonts from font_list().
  1772. for (const Font& font : font_list().GetFonts()) {
  1773. internal::TextRunHarfBuzz::FontParams test_font_params = font_params;
  1774. if (test_font_params.SetRenderParamsRematchFont(
  1775. font, font.GetFontRenderParams()) &&
  1776. !FontWasAlreadyTried(test_font_params.skia_face,
  1777. &fallback_fonts_already_tried)) {
  1778. ShapeRunsWithFont(text, test_font_params, &runs);
  1779. MarkFontAsTried(test_font_params.skia_face,
  1780. &fallback_fonts_already_tried);
  1781. fallback_font_candidates.push_back(font);
  1782. }
  1783. if (runs.empty()) {
  1784. RecordShapeRunsFallback(ShapeRunFallback::NO_FALLBACK);
  1785. return;
  1786. }
  1787. }
  1788. const Font& primary_font = font_list().GetPrimaryFont();
  1789. // Find fallback fonts for the remaining runs using a worklist algorithm. Try
  1790. // to shape the first run by using GetFallbackFont(...) and then try shaping
  1791. // other runs with the same font. If the first font can't be shaped, remove it
  1792. // and continue with the remaining runs until the worklist is empty. The
  1793. // fallback font returned by GetFallbackFont(...) depends on the text of the
  1794. // run and the results may differ between runs.
  1795. std::vector<internal::TextRunHarfBuzz*> remaining_unshaped_runs;
  1796. while (!runs.empty()) {
  1797. Font fallback_font(primary_font);
  1798. bool fallback_found;
  1799. internal::TextRunHarfBuzz* current_run = *runs.begin();
  1800. {
  1801. SCOPED_UMA_HISTOGRAM_LONG_TIMER("RenderTextHarfBuzz.GetFallbackFontTime");
  1802. TRACE_EVENT1("ui", "RenderTextHarfBuzz::GetFallbackFont", "script",
  1803. TRACE_STR_COPY(uscript_getShortName(font_params.script)));
  1804. const base::StringPiece16 run_text(&text[current_run->range.start()],
  1805. current_run->range.length());
  1806. fallback_found =
  1807. GetFallbackFont(primary_font, locale_, run_text, &fallback_font);
  1808. }
  1809. if (fallback_found) {
  1810. internal::TextRunHarfBuzz::FontParams test_font_params = font_params;
  1811. if (test_font_params.SetRenderParamsOverrideSkiaFaceFromFont(
  1812. fallback_font, fallback_font.GetFontRenderParams()) &&
  1813. !FontWasAlreadyTried(test_font_params.skia_face,
  1814. &fallback_fonts_already_tried)) {
  1815. ShapeRunsWithFont(text, test_font_params, &runs);
  1816. MarkFontAsTried(test_font_params.skia_face,
  1817. &fallback_fonts_already_tried);
  1818. }
  1819. }
  1820. // Remove the first run if not fully shaped with its associated fallback
  1821. // font.
  1822. if (!runs.empty() && runs[0] == current_run) {
  1823. remaining_unshaped_runs.push_back(current_run);
  1824. runs.erase(runs.begin());
  1825. }
  1826. }
  1827. runs.swap(remaining_unshaped_runs);
  1828. if (runs.empty()) {
  1829. RecordShapeRunsFallback(ShapeRunFallback::FALLBACK);
  1830. return;
  1831. }
  1832. std::vector<Font> fallback_font_list;
  1833. {
  1834. SCOPED_UMA_HISTOGRAM_LONG_TIMER("RenderTextHarfBuzz.GetFallbackFontsTime");
  1835. TRACE_EVENT1("ui", "RenderTextHarfBuzz::GetFallbackFonts", "script",
  1836. TRACE_STR_COPY(uscript_getShortName(font_params.script)));
  1837. fallback_font_list = GetFallbackFonts(primary_font);
  1838. #if BUILDFLAG(IS_WIN)
  1839. // Append fonts in the fallback list of the fallback fonts.
  1840. // TODO(tapted): Investigate whether there's a case that benefits from this
  1841. // on Mac.
  1842. for (const auto& fallback_font : fallback_font_candidates) {
  1843. std::vector<Font> fallback_fonts = GetFallbackFonts(fallback_font);
  1844. fallback_font_list.insert(fallback_font_list.end(),
  1845. fallback_fonts.begin(), fallback_fonts.end());
  1846. }
  1847. // Add Segoe UI and its associated linked fonts to the fallback font list to
  1848. // ensure that the fallback list covers the basic cases.
  1849. // http://crbug.com/467459. On some Windows configurations the default font
  1850. // could be a raster font like System, which would not give us a reasonable
  1851. // fallback font list.
  1852. Font segoe("Segoe UI", 13);
  1853. if (!FontWasAlreadyTried(segoe.platform_font()->GetNativeSkTypeface(),
  1854. &fallback_fonts_already_tried)) {
  1855. std::vector<Font> default_fallback_families = GetFallbackFonts(segoe);
  1856. fallback_font_list.insert(fallback_font_list.end(),
  1857. default_fallback_families.begin(),
  1858. default_fallback_families.end());
  1859. }
  1860. #endif
  1861. }
  1862. // Use a set to track the fallback fonts and avoid duplicate entries.
  1863. SCOPED_UMA_HISTOGRAM_LONG_TIMER(
  1864. "RenderTextHarfBuzz.ShapeRunsWithFallbackFontsTime");
  1865. TRACE_EVENT1("ui", "RenderTextHarfBuzz::ShapeRunsWithFallbackFonts",
  1866. "fonts_count", fallback_font_list.size());
  1867. // Try shaping with the fallback fonts.
  1868. for (const auto& font : fallback_font_list) {
  1869. std::string font_name = font.GetFontName();
  1870. FontRenderParamsQuery query;
  1871. query.families.push_back(font_name);
  1872. query.pixel_size = font_params.font_size;
  1873. query.style = font_params.italic ? Font::ITALIC : 0;
  1874. FontRenderParams fallback_render_params = GetFontRenderParams(query, NULL);
  1875. internal::TextRunHarfBuzz::FontParams test_font_params = font_params;
  1876. if (test_font_params.SetRenderParamsOverrideSkiaFaceFromFont(
  1877. font, fallback_render_params) &&
  1878. !FontWasAlreadyTried(test_font_params.skia_face,
  1879. &fallback_fonts_already_tried)) {
  1880. ShapeRunsWithFont(text, test_font_params, &runs);
  1881. MarkFontAsTried(test_font_params.skia_face,
  1882. &fallback_fonts_already_tried);
  1883. }
  1884. if (runs.empty()) {
  1885. TRACE_EVENT_INSTANT2("ui", "RenderTextHarfBuzz::FallbackFont",
  1886. TRACE_EVENT_SCOPE_THREAD, "font_name",
  1887. TRACE_STR_COPY(font_name.c_str()),
  1888. "primary_font_name", primary_font.GetFontName());
  1889. RecordShapeRunsFallback(ShapeRunFallback::FALLBACKS);
  1890. return;
  1891. }
  1892. }
  1893. for (internal::TextRunHarfBuzz*& run : runs) {
  1894. if (run->shape.missing_glyph_count == std::numeric_limits<size_t>::max()) {
  1895. run->shape.glyph_count = 0;
  1896. run->shape.width = 0.0f;
  1897. }
  1898. }
  1899. RecordShapeRunsFallback(ShapeRunFallback::FAILED);
  1900. }
  1901. void RenderTextHarfBuzz::ShapeRunsWithFont(
  1902. const std::u16string& text,
  1903. const internal::TextRunHarfBuzz::FontParams& font_params,
  1904. std::vector<internal::TextRunHarfBuzz*>* in_out_runs) {
  1905. // ShapeRunWithFont can be extremely slow, so use cached results if possible.
  1906. // Only do this on the UI thread, to avoid synchronization overhead (and
  1907. // because almost all calls are on the UI thread. Also avoid caching long
  1908. // strings, to avoid blowing up the cache size.
  1909. constexpr size_t kMaxRunLengthToCache = 25;
  1910. static base::NoDestructor<internal::ShapeRunCache> cache;
  1911. std::vector<internal::TextRunHarfBuzz*> runs_with_missing_glyphs;
  1912. for (internal::TextRunHarfBuzz*& run : *in_out_runs) {
  1913. // First do a cache lookup.
  1914. bool can_use_cache = base::CurrentUIThread::IsSet() &&
  1915. run->range.length() <= kMaxRunLengthToCache;
  1916. bool found_in_cache = false;
  1917. const internal::ShapeRunWithFontInput cache_key(
  1918. text, font_params, run->range, obscured(), glyph_width_for_test_,
  1919. obscured_glyph_spacing(), subpixel_rendering_suppressed());
  1920. if (can_use_cache) {
  1921. auto found = cache.get()->Get(cache_key);
  1922. if (found != cache.get()->end()) {
  1923. run->UpdateFontParamsAndShape(font_params, found->second);
  1924. found_in_cache = true;
  1925. }
  1926. }
  1927. // If that fails, compute the shape of the run, and add the result to the
  1928. // cache.
  1929. // TODO(ccameron): Coalesce calls to ShapeRunsWithFont when possible.
  1930. if (!found_in_cache) {
  1931. internal::TextRunHarfBuzz::ShapeOutput output;
  1932. ShapeRunWithFont(cache_key, &output);
  1933. run->UpdateFontParamsAndShape(font_params, output);
  1934. if (can_use_cache)
  1935. cache.get()->Put(cache_key, output);
  1936. }
  1937. // Check to see if we still have missing glyphs.
  1938. if (run->shape.missing_glyph_count)
  1939. runs_with_missing_glyphs.push_back(run);
  1940. }
  1941. in_out_runs->swap(runs_with_missing_glyphs);
  1942. }
  1943. void RenderTextHarfBuzz::EnsureLayoutRunList() {
  1944. // Update layout run list if the device scale factor has changed since the
  1945. // layout run list was last updated, as changes in device scale factor change
  1946. // subpixel positioning, at least on Linux and Chrome OS.
  1947. const float device_scale_factor = GetFontRenderParamsDeviceScaleFactor();
  1948. if (update_layout_run_list_ || device_scale_factor_ != device_scale_factor) {
  1949. device_scale_factor_ = device_scale_factor;
  1950. layout_run_list_.Reset();
  1951. const std::u16string& text = GetLayoutText();
  1952. if (!text.empty())
  1953. ItemizeAndShapeText(text, &layout_run_list_);
  1954. display_run_list_.reset();
  1955. update_display_text_ = true;
  1956. update_layout_run_list_ = false;
  1957. }
  1958. if (update_display_text_) {
  1959. set_shaped_text(nullptr);
  1960. UpdateDisplayText(multiline() ? 0 : layout_run_list_.width());
  1961. update_display_text_ = false;
  1962. update_display_run_list_ = text_elided();
  1963. }
  1964. }
  1965. // Returns the current run list, |display_run_list_| if the text is elided, or
  1966. // |layout_run_list_| otherwise.
  1967. internal::TextRunList* RenderTextHarfBuzz::GetRunList() {
  1968. DCHECK(!update_layout_run_list_);
  1969. DCHECK(!update_display_run_list_);
  1970. return text_elided() ? display_run_list_.get() : &layout_run_list_;
  1971. }
  1972. const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const {
  1973. return const_cast<RenderTextHarfBuzz*>(this)->GetRunList();
  1974. }
  1975. bool RenderTextHarfBuzz::IsValidDisplayRange(Range display_range) {
  1976. // The |display_text_| is an elided version of |layout_text_|. Removing
  1977. // codepoints from the text may break the conversion for codepoint offsets
  1978. // between text to display_text offset. For elding behaviors that truncate
  1979. // codepoint at the end, the conversion will work just fine. But for eliding
  1980. // behavior that truncate at the beginning of middle of the text, the offsets
  1981. // are completely wrong and should not be used.
  1982. // TODO(http://crbug.com/1085014): Fix eliding for the broken cases.
  1983. switch (elide_behavior()) {
  1984. case NO_ELIDE:
  1985. case FADE_TAIL:
  1986. return display_range.IsBoundedBy(Range(0, GetDisplayText().length()));
  1987. case TRUNCATE:
  1988. case ELIDE_TAIL:
  1989. return display_range.IsBoundedBy(Range(0, GetLayoutText().length()));
  1990. case ELIDE_HEAD:
  1991. case ELIDE_MIDDLE:
  1992. case ELIDE_EMAIL:
  1993. return !text_elided();
  1994. }
  1995. }
  1996. bool RenderTextHarfBuzz::GetDecoratedTextForRange(
  1997. const Range& range,
  1998. DecoratedText* decorated_text) {
  1999. if (obscured())
  2000. return false;
  2001. EnsureLayout();
  2002. decorated_text->attributes.clear();
  2003. decorated_text->text = GetTextFromRange(range);
  2004. const internal::TextRunList* run_list = GetRunList();
  2005. for (size_t i = 0; i < run_list->size(); i++) {
  2006. const internal::TextRunHarfBuzz& run = *run_list->runs()[i];
  2007. const Range intersection = range.Intersect(run.range);
  2008. DCHECK(!intersection.is_reversed());
  2009. if (!intersection.is_empty()) {
  2010. int style = Font::NORMAL;
  2011. if (run.font_params.italic)
  2012. style |= Font::ITALIC;
  2013. if (run.font_params.underline || run.font_params.heavy_underline)
  2014. style |= Font::UNDERLINE;
  2015. // Get range relative to the decorated text.
  2016. DecoratedText::RangedAttribute attribute(
  2017. Range(intersection.start() - range.GetMin(),
  2018. intersection.end() - range.GetMin()),
  2019. run.font_params.font.Derive(0, style, run.font_params.weight));
  2020. attribute.strike = run.font_params.strike;
  2021. decorated_text->attributes.push_back(attribute);
  2022. }
  2023. }
  2024. return true;
  2025. }
  2026. } // namespace gfx