HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
unicode_line_break.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
8#pragma once
9
10#include "unicode_general_category.hpp"
11#include "unicode_grapheme_cluster_break.hpp"
13#include "unicode_break_opportunity.hpp"
14#include "../utility.hpp"
15#include "../cast.hpp"
16#include "../assert.hpp"
17#include "../math.hpp"
18#include <cstdint>
19#include <vector>
20#include <algorithm>
21#include <numeric>
22
23// Windows.h adds a "IN" macro that is used in this enum.
24#ifdef IN
25#undef IN
26#endif
27
28namespace hi::inline v1 {
29
36enum class unicode_line_break_class : uint8_t {
37 BK, // Mandatory Break NL, PARAGRAPH SEPARATOR Cause a line break (after)
38 CR, // Carriage Return CR Cause a line break (after), except between CR and LF
39 LF, // Line Feed LF Cause a line break (after)
40 CM, // Combining Mark Combining marks, control codes Prohibit a line break between the character and the preceding character
41 NL, // Next Line NEL Cause a line break (after)
42 SG, // Surrogate Surrogates Do not occur in well-formed text
43 WJ, // Word Joiner WJ Prohibit line breaks before and after
44 ZW, // Zero Width Space ZWSP Provide a break opportunity
45 GL, // Non-breaking (Glue) CGJ, NBSP, ZWNBSP Prohibit line breaks before and after
46 SP, // Space SPACE Enable indirect line breaks
47 ZWJ, // Zero Width Joiner Zero Width Joiner Prohibit line breaks within joiner sequences Break Opportunities
48
49 B2, // Break Opportunity Before and After Em dash Provide a line break opportunity before and after the character
50 BA, // Break After Spaces, hyphens Generally provide a line break opportunity after the character
51 BB, // Break Before Punctuation used in dictionaries Generally provide a line break opportunity before the character
52 HY, // Hyphen HYPHEN-MINUS Provide a line break opportunity after the character, except in numeric context
53 CB, // Contingent Break Opportunity Inline objects Provide a line break opportunity contingent on additional information
54 // Characters Prohibiting Certain Breaks
55
56 CL, // Close Punctuation Prohibit line breaks before
57 CP, // Close Parenthesis ')', ']' Prohibit line breaks before
58 EX, // Exclamation/Interrogation '!', '?', etc. Prohibit line breaks before
59 IN, // Inseparable Leaders Allow only indirect line breaks between pairs
60 NS, // Nonstarter. Allow only indirect line breaks before
61 OP, // Open Punctuation '(', '[', '{', etc. Prohibit line breaks after
62 QU, // Quotation Quotation marks Act like they are both opening and closing Numeric Context
63
64 IS, // Infix Numeric Separator . , Prevent breaks after any and before numeric
65 NU, // Numeric Digits Form numeric expressions for line breaking purposes
66 PO, // Postfix Numeric. Do not break following a numeric expression
67 PR, // Prefix Numeric. Do not break in front of a numeric expression
68 SY, // Symbols Allowing Break After / Prevent a break before, and allow a break after XX Characters
69
70 AI, // Ambiguous (Alphabetic or Ideographic) Characters with Ambiguous East Asian Width Act like AL when the resolved EAW
71 // is N; XXwise, act as ID
72 AL, // Alphabetic Alphabets and regular symbols Are alphabetic characters or symbols that are used with alphabetic
73 // characters
74 CJ, // Conditional Japanese Starter Small kana Treat as NS or ID for strict or normal breaking.
75 EB, // Emoji Base All emoji allowing modifiers Do not break from following Emoji Modifier
76 EM, // Emoji Modifier Skin tone modifiers Do not break from preceding Emoji Base
77 H2, // Hangul LV Syllable Hangul Form Korean syllable blocks
78 H3, // Hangul LVT Syllable Hangul Form Korean syllable blocks
79 HL, // Hebrew Letter Hebrew Do not break around a following hyphen; otherwise act as Alphabetic
80 ID, // Ideographic Ideographs Break before or after, except in some numeric context
81 JL, // Hangul
82 L, // Jamo Conjoining jamo Form Korean syllable blocks
83 JV, // Hangul
84 V, // Jamo Conjoining jamo Form Korean syllable blocks
85 JT, // Hangul
86 T, // Jamo Conjoining jamo Form Korean syllable blocks
87 RI, // Regional Indicator REGIONAL INDICATOR SYMBOL LETTER A..Z Keep pairs together.For pairs, break before and after XX
88 // classes
89 SA, // Complex Context Dependent(South East Asian) South East Asian :Thai,Lao,Khmer Provide a line break opportunity
90 // contingent on additional, language - specific context analysis
91 XX, // Unknown Most unassigned, private - use Have as yet unknown line breaking behavior or unassigned code positions
92};
93
94namespace detail {
95
99 unicode_line_break_class original_class = unicode_line_break_class::XX;
100 unicode_line_break_class current_class = unicode_line_break_class::XX;
101 bool is_extended_pictographic = false;
102 bool is_Cn = false;
103 unicode_east_asian_width east_asian_width = unicode_east_asian_width::A;
104
105 constexpr unicode_line_break_info() noexcept = default;
106 constexpr unicode_line_break_info(unicode_line_break_info const&) noexcept = default;
107 constexpr unicode_line_break_info(unicode_line_break_info&&) noexcept = default;
108 constexpr unicode_line_break_info& operator=(unicode_line_break_info const&) noexcept = default;
109 constexpr unicode_line_break_info& operator=(unicode_line_break_info&&) noexcept = default;
110
111 constexpr explicit unicode_line_break_info(
112 unicode_line_break_class break_class,
113 bool is_Cn,
114 bool is_extended_pictographic,
115 unicode_east_asian_width east_asian_width) noexcept :
116 original_class(break_class),
117 current_class(break_class),
118 is_Cn(is_Cn),
119 is_extended_pictographic(is_extended_pictographic),
120 east_asian_width(east_asian_width)
121 {
122 }
123
124 constexpr explicit operator unicode_line_break_class() const noexcept
125 {
126 return current_class;
127 }
128
129 constexpr unicode_line_break_info& operator|=(unicode_line_break_class rhs) noexcept
130 {
131 current_class = rhs;
132 return *this;
133 }
134
135 [[nodiscard]] constexpr bool operator==(unicode_line_break_class rhs) const noexcept
136 {
137 return current_class == rhs;
138 }
139
140 [[nodiscard]] constexpr bool operator==(unicode_east_asian_width rhs) const noexcept
141 {
142 return east_asian_width == rhs;
143 }
144};
145
147using unicode_line_break_info_iterator = unicode_line_break_info_vector::iterator;
148using unicode_line_break_info_const_iterator = unicode_line_break_info_vector::const_iterator;
149
150template<typename It, typename ItEnd, typename DescriptionFunc>
151[[nodiscard]] constexpr std::vector<unicode_line_break_info>
152unicode_LB1(It first, ItEnd last, DescriptionFunc const& description_func) noexcept
153{
155 r.reserve(std::distance(first, last));
156
157 for (auto it = first; it != last; ++it) {
158 hilet& description = description_func(*it);
159 hilet break_class = description.line_break_class();
160 hilet general_category = description.general_category();
161
162 hilet resolved_break_class = [&]() {
163 switch (break_class) {
164 using enum unicode_line_break_class;
165 case AI:
166 case SG:
167 case XX:
168 return AL;
169 case CJ:
170 return NS;
171 case SA:
172 return is_Mn_or_Mc(general_category) ? CM : AL;
173 default:
174 return break_class;
175 }
176 }();
177
178 r.emplace_back(
179 resolved_break_class,
180 general_category == unicode_general_category::Cn,
181 description.grapheme_cluster_break() == unicode_grapheme_cluster_break::Extended_Pictographic,
182 description.east_asian_width());
183 }
184
185 return r;
186}
187
188[[nodiscard]] constexpr void unicode_LB2_3(unicode_break_vector& opportunities) noexcept
189{
190 hi_axiom(not opportunities.empty());
191 // LB2
192 opportunities.front() = unicode_break_opportunity::no;
193 // LB3
194 opportunities.back() = unicode_break_opportunity::mandatory;
195}
196
197template<typename MatchFunc>
198constexpr void unicode_LB_walk(
199 unicode_break_vector& opportunities,
201 MatchFunc match_func) noexcept
202{
203 using enum unicode_line_break_class;
204
205 if (infos.empty()) {
206 return;
207 }
208
209 auto cur = infos.begin();
210 hilet last = infos.end() - 1;
211 hilet last2 = infos.end();
212 auto opportunity = opportunities.begin() + 1;
213
214 auto cur_sp_class = XX;
215 auto cur_nu_class = XX;
216 auto prev_class = XX;
217 auto num_ri = 0_uz;
218 while (cur != last) {
219 hilet next = cur + 1;
220 hilet cur_class = unicode_line_break_class{*cur};
221 hilet next2_class = cur + 2 == last2 ? XX : unicode_line_break_class{*(cur + 2)};
222
223 // Keep track of classes followed by zero or more SP.
224 if (cur_class != SP) {
225 cur_sp_class = cur_class;
226 }
227
228 // Keep track of a "NU (NU|SY|IS)*" and "NU (NU|SY|IS)* (CL|CP)?".
229 if (cur_nu_class == CL) {
230 // Only a single CL|CP class may be at the end, then the number is closed.
231 cur_nu_class = XX;
232 } else if (cur_nu_class == NU) {
233 if (cur_class == CL or cur_class == CP) {
234 cur_nu_class = CL;
235 } else if (cur_class != NU and cur_class != SY and cur_class != IS) {
236 cur_nu_class = XX;
237 }
238 } else if (cur_class == NU) {
239 cur_nu_class = NU;
240 }
241
242 // Keep track of consecutive RI, but only count the actual RIs.
243 if (cur->original_class == RI) {
244 ++num_ri;
245 } else if (*cur != RI) {
246 num_ri = 0;
247 }
248
249 if (*opportunity == unicode_break_opportunity::unassigned) {
250 *opportunity = match_func(prev_class, cur, next, next2_class, cur_sp_class, cur_nu_class, num_ri);
251 }
252
253 prev_class = cur_class;
254 cur = next;
255 ++opportunity;
256 }
257}
258
259constexpr void unicode_LB4_8a(unicode_break_vector& opportunities, std::vector<unicode_line_break_info> const& infos) noexcept
260{
261 unicode_LB_walk(
262 opportunities, infos, [](hilet prev, hilet cur, hilet next, hilet next2, hilet cur_sp, hilet cur_nu, hilet num_ri) {
263 using enum unicode_break_opportunity;
264 using enum unicode_line_break_class;
265 if (*cur == BK) {
266 return mandatory; // LB4: 4.0
267 } else if (*cur == CR and *next == LF) {
268 return no; // LB5: 5.01
269 } else if (*cur == CR or *cur == LF or *cur == NL) {
270 return mandatory; // LB5: 5.02, 5.03, 5.04
271 } else if (*next == BK or *next == CR or *next == LF or *next == NL) {
272 return no; // LB6: 6.0
273 } else if (*next == SP or *next == ZW) {
274 return no; // LB7: 7.01, 7.02
275 } else if (cur_sp == ZW) {
276 return yes; // LB8: 8.0
277 } else if (*cur == ZWJ) {
278 return no; // LB8a: 8.1
279 } else {
280 return unassigned;
281 }
282 });
283}
284
285constexpr void unicode_LB9(unicode_break_vector& opportunities, std::vector<unicode_line_break_info>& infos) noexcept
286{
287 using enum unicode_line_break_class;
288 using enum unicode_break_opportunity;
289
290 if (infos.empty()) {
291 return;
292 }
293
294 auto cur = infos.begin();
295 hilet last = infos.end() - 1;
296 auto opportunity = opportunities.begin() + 1;
297
298 auto X = XX;
299 while (cur != last) {
300 hilet next = cur + 1;
301
302 if ((*cur == CM or *cur == ZWJ) and X != XX) {
303 // Treat all CM/ZWJ as X (if there is an X).
304 *cur |= X;
305 } else {
306 // Reset X on non-CM/ZWJ.
307 X = XX;
308 }
309
310 if ((*cur != BK and *cur != CR and *cur != LF and *cur != NL and *cur != SP and *cur != ZW) and
311 (*next == CM or *next == ZWJ)) {
312 // [^BK CR LF NL SP ZW] x [CM ZWJ]*
313 *opportunity = no;
314
315 if (X == XX) {
316 // The first character of [^BK CR LF NL SP ZW] x [CM ZWJ]* => X
317 X = static_cast<unicode_line_break_class>(*cur);
318 }
319 }
320
321 cur = next;
322 ++opportunity;
323 }
324}
325
326constexpr void unicode_LB10(std::vector<unicode_line_break_info>& infos) noexcept
327{
328 using enum unicode_line_break_class;
329
330 for (auto& x : infos) {
331 if (x == CM or x == ZWJ) {
332 x |= AL;
333 }
334 }
335}
336
337constexpr void unicode_LB11_31(unicode_break_vector& opportunities, std::vector<unicode_line_break_info> const& infos) noexcept
338{
339 unicode_LB_walk(
340 opportunities, infos, [&](hilet prev, hilet cur, hilet next, hilet next2, hilet cur_sp, hilet cur_nu, hilet num_ri) {
341 using enum unicode_break_opportunity;
342 using enum unicode_line_break_class;
343 using enum unicode_east_asian_width;
344
345 if (*cur == WJ or *next == WJ) {
346 return no; // LB11: 11.01, 11.02
347 } else if (*cur == GL) {
348 return no; // LB12: 12.0
349 } else if (*cur != SP and *cur != BA and *cur != HY and *next == GL) {
350 return no; // LB12a: 12.1
351 } else if (*next == CL or *next == CP or *next == EX or *next == IS or *next == SY) {
352 return no; // LB13: 13.0
353 } else if (cur_sp == OP) {
354 return no; // LB14: 14.0
355 } else if (cur_sp == QU and *next == OP) {
356 return no; // LB15: 15.0
357 } else if ((cur_sp == CL or cur_sp == CP) and *next == NS) {
358 return no; // LB16: 16.0
359 } else if (cur_sp == B2 and *next == B2) {
360 return no; // LB17: 17.0
361 } else if (*cur == SP) {
362 return yes; // LB18: 18.0
363 } else if (*cur == QU or *next == QU) {
364 return no; // LB19: 19.01, 19.02
365 } else if (*cur == CB or *next == CB) {
366 return yes; // LB20: 20.01, 20.02
367 } else if (*cur == BB or *next == BA or *next == HY or *next == NS) {
368 return no; // LB21: 21.01, 21.02, 21.03, 21.04
369 } else if (prev == HL and (*cur == HY or *cur == BA)) {
370 return no; // LB21a: 21.1
371 } else if (*cur == SY and *next == HL) {
372 return no; // LB21b: 21.2
373 } else if (*next == IN) {
374 return no; // LB22: 22.0
375 } else if ((*cur == AL or *cur == HL) and *next == NU) {
376 return no; // LB23: 23.02
377 } else if (*cur == NU and (*next == AL or *next == HL)) {
378 return no; // LB23: 23.03
379 } else if (*cur == PR and (*next == ID or *next == EB or *next == EM)) {
380 return no; // LB23a: 23.12
381 } else if ((*cur == ID or *cur == EB or *cur == EM) and *next == PO) {
382 return no; // LB23a: 23.13
383 } else if ((*cur == PR or *cur == PO) and (*next == AL or *next == HL)) {
384 return no; // LB24: 24.02
385 } else if ((*cur == AL or *cur == HL) and (*next == PR or *next == PO)) {
386 return no; // LB24: 24.03
387 } else if (
388 (*cur == PR or *cur == PO) and ((*next == OP and next2 == NU) or (*next == HY and next2 == NU) or *next == NU)) {
389 return no; // LB25: 25.01
390 } else if ((*cur == OP or *cur == HY) and *next == NU) {
391 return no; // LB25: 25.02
392 } else if (*cur == NU and (*next == NU or *next == SY or *next == IS)) {
393 return no; // LB25: 25.03
394 } else if (cur_nu == NU and (*next == NU or *next == SY or *next == IS or *next == CL or *next == CP)) {
395 return no; // LB25: 25.04
396 } else if ((cur_nu == NU or cur_nu == CL) and (*next == PO or *next == PR)) {
397 return no; // LB25: 25.05
398 } else if (*cur == JL and (*next == JL or *next == JV or *next == H2 or *next == H3)) {
399 return no; // LB26: 26.01
400 } else if ((*cur == JV or *cur == H2) and (*next == JV or *next == JT)) {
401 return no; // LB26: 26.02
402 } else if ((*cur == JT or *cur == H3) and *next == JT) {
403 return no; // LB26: 26.03
404 } else if ((*cur == JL or *cur == JV or *cur == JT or *cur == H2 or *cur == H3) and *next == PO) {
405 return no; // LB27: 27.01
406 } else if (*cur == PR and (*next == JL or *next == JV or *next == JT or *next == H2 or *next == H3)) {
407 return no; // LB27: 27.02
408 } else if ((*cur == AL or *cur == HL) and (*next == AL or *next == HL)) {
409 return no; // LB28: 28.0
410 } else if (*cur == IS and (*next == AL or *next == HL)) {
411 return no; // LB29: 29.0
412 } else if ((*cur == AL or *cur == HL or *cur == NU) and (*next == OP and *next != F and *next != W and *next != H)) {
413 return no; // LB30: 30.01
414 } else if ((*cur == CP and *cur != F and *cur != W and *cur != H) and (*next == AL or *next == HL or *next == NU)) {
415 return no; // LB30: 30.02
416 } else if (*cur == RI and *next == RI and (num_ri % 2) == 1) {
417 return no; // LB30a: 30.11, 30.12, 30.13
418 } else if (*cur == EB and *next == EM) {
419 return no; // LB30b: 30.21
420 } else if (cur->is_extended_pictographic and cur->is_Cn and *next == EM) {
421 return no; // LB30b: 30.22
422 } else {
423 return yes; // LB31: 999.0
424 }
425 });
426}
427
434[[nodiscard]] constexpr float
435unicode_LB_width(std::vector<float>::const_iterator first, std::vector<float>::const_iterator last) noexcept
436{
437 if (first == last) {
438 return 0.0f;
439 }
440
441 auto rfirst = std::make_reverse_iterator(last);
442 auto rlast = std::make_reverse_iterator(first);
443
444 auto it = std::find_if(rfirst, rlast, [](hilet& width) {
445 return width >= 0.0;
446 });
447 return std::accumulate(it, rlast, 0.0f, [](float acc, hilet& width) {
448 return acc + abs(width);
449 });
450}
451
458[[nodiscard]] constexpr float unicode_LB_width(std::vector<float> const& widths, std::vector<size_t> const& lengths)
459{
460 auto max_width = 0.0f;
461 auto it = widths.begin();
462 for (auto length : lengths) {
463 inplace_max(max_width, unicode_LB_width(it, it + length));
464 it += length;
465 }
466 return max_width;
467}
468
476[[nodiscard]] constexpr bool
477unicode_LB_width_check(std::vector<float> const& widths, std::vector<size_t> const& lengths, float maximum_line_width) noexcept
478{
479 auto it = widths.begin();
480 for (auto length : lengths) {
481 if (unicode_LB_width(it, it + length) > maximum_line_width) {
482 return false;
483 }
484 it += length;
485 }
486 return true;
487}
488
493[[nodiscard]] constexpr std::vector<size_t> unicode_LB_mandatory_lines(unicode_break_vector const& opportunities) noexcept
494{
495 auto r = std::vector<size_t>{};
496
497 auto length = 0_uz;
498 for (auto it = opportunities.begin() + 1; it != opportunities.end(); ++it) {
499 ++length;
500 if (*it == unicode_break_opportunity::mandatory) {
501 r.push_back(length);
502 length = 0_uz;
503 }
504 }
505
506 return r;
507}
508
513[[nodiscard]] constexpr std::vector<size_t> unicode_LB_optional_lines(unicode_break_vector const& opportunities) noexcept
514{
515 auto r = std::vector<size_t>{};
516
517 auto length = 0_uz;
518 for (auto it = opportunities.begin() + 1; it != opportunities.end(); ++it) {
519 ++length;
520 if (*it != unicode_break_opportunity::no) {
521 r.push_back(length);
522 length = 0_uz;
523 }
524 }
525
526 return r;
527}
528
529[[nodiscard]] constexpr unicode_break_const_iterator unicode_LB_fast_fit_line(
530 unicode_break_const_iterator opportunity_it,
531 std::vector<float>::const_iterator width_it,
532 float maximum_line_width) noexcept
533{
534 using enum unicode_break_opportunity;
535
536 auto width = 0.0f;
537 auto end_of_line = opportunity_it;
538 while (true) {
539 width += abs(*width_it);
540 if (width > maximum_line_width) {
541 // This character makes the width too long.
542 return end_of_line;
543
544 } else if (*opportunity_it == mandatory) {
545 // This character is an end-of-line.
546 return opportunity_it;
547
548 } else if (*opportunity_it == yes) {
549 // This character is a valid break opportunity.
550 end_of_line = opportunity_it;
551 }
552
553 ++opportunity_it;
554 ++width_it;
555 }
556 hi_unreachable();
557}
558
559[[nodiscard]] constexpr unicode_break_const_iterator unicode_LB_slow_fit_line(
560 unicode_break_const_iterator first,
561 unicode_break_const_iterator end_of_line,
562 std::vector<float>::const_iterator first_width,
563 float maximum_line_width) noexcept
564{
565 using enum unicode_break_opportunity;
566
567 // Carefully look forward for a break opportunity.
568 auto it = end_of_line;
569 while (true) {
570 hilet num_characters = std::distance(first, it + 1);
571 hilet line_width = unicode_LB_width(first_width, first_width + num_characters);
572
573 if (line_width <= maximum_line_width) {
574 if (*it == mandatory) {
575 // The next mandatory break fits in the maximum width.
576 return it;
577
578 } else if (*it == yes) {
579 // The next break opportunity fits in the maximum width.
580 end_of_line = it;
581 }
582 } else {
583 // This break opportunity doesn't fit within the maximum width. Use the previous break opportunity.
584 return end_of_line;
585 }
586
587 ++it;
588 }
589 hi_unreachable();
590}
591
592[[nodiscard]] constexpr unicode_break_const_iterator
593unicode_LB_finish_fit_line(unicode_break_const_iterator first, unicode_break_const_iterator end_of_line) noexcept
594{
595 if (first == end_of_line) {
596 // We couldn't break the line to fit the maximum line width.
597 while (*end_of_line == unicode_break_opportunity::no) {
598 ++end_of_line;
599 }
600 }
601
602 // Return iterator past the end-of-line.
603 return end_of_line + 1;
604}
605
611 unicode_break_vector const& opportunities,
612 std::vector<float> const& widths,
613 float maximum_line_width) noexcept
614{
615 using enum unicode_break_opportunity;
616
617 auto r = std::vector<size_t>{};
618 if (widths.empty()) {
619 return r;
620 }
621
622 auto opportunity_it = opportunities.begin() + 1;
623 auto width_it = widths.begin();
624 while (width_it != widths.end()) {
625 // First quickly find when the line is too long.
626 auto opportunity_eol = unicode_LB_fast_fit_line(opportunity_it, width_it, maximum_line_width);
627 opportunity_eol = unicode_LB_slow_fit_line(opportunity_it, opportunity_eol, width_it, maximum_line_width);
628 opportunity_eol = unicode_LB_finish_fit_line(opportunity_it, opportunity_eol);
629
630 hilet num_characters = std::distance(opportunity_it, opportunity_eol);
631 r.push_back(num_characters);
632 opportunity_it += num_characters;
633 width_it += num_characters;
634 }
635
636 return r;
637}
638
647[[nodiscard]] constexpr std::pair<float, std::vector<size_t>>
648unicode_LB_maximum_width(unicode_break_vector const& opportunities, std::vector<float> const& char_widths)
649{
650 auto line_lengths = detail::unicode_LB_mandatory_lines(opportunities);
651 hilet width = detail::unicode_LB_width(char_widths, line_lengths);
652 return {width, std::move(line_lengths)};
653}
654
663[[nodiscard]] constexpr std::pair<float, std::vector<size_t>>
664unicode_LB_minimum_width(unicode_break_vector const& opportunities, std::vector<float> const& char_widths)
665{
666 auto line_lengths = detail::unicode_LB_optional_lines(opportunities);
667 hilet width = detail::unicode_LB_width(char_widths, line_lengths);
668 return {width, std::move(line_lengths)};
669}
670
680[[nodiscard]] constexpr std::pair<float, std::vector<size_t>>
681unicode_LB_width(unicode_break_vector const& opportunities, std::vector<float> const& char_widths, float maximum_line_width)
682{
683 auto line_lengths = detail::unicode_LB_fit_lines(opportunities, char_widths, maximum_line_width);
684 hilet width = detail::unicode_LB_width(char_widths, line_lengths);
685 return {width, std::move(line_lengths)};
686}
687
688} // namespace detail
689
697template<typename It, typename ItEnd, typename DescriptionFunc>
698[[nodiscard]] inline unicode_break_vector
699unicode_line_break(It first, ItEnd last, DescriptionFunc const& description_func) noexcept
700{
701 auto size = narrow_cast<size_t>(std::distance(first, last));
702 auto r = unicode_break_vector{size + 1, unicode_break_opportunity::unassigned};
703
704 auto infos = detail::unicode_LB1(first, last, description_func);
705 detail::unicode_LB2_3(r);
706 detail::unicode_LB4_8a(r, infos);
707 detail::unicode_LB9(r, infos);
708 detail::unicode_LB10(infos);
709 detail::unicode_LB11_31(r, infos);
710 return r;
711}
712
720[[nodiscard]] constexpr std::vector<size_t>
721unicode_line_break(unicode_break_vector const& opportunities, std::vector<float> const& widths, float maximum_line_width)
722{
723 // See if the lines after mandatory breaks will fit the width and return.
724 auto r = detail::unicode_LB_mandatory_lines(opportunities);
725 if (detail::unicode_LB_width_check(widths, r, maximum_line_width)) {
726 return r;
727 }
728
729 r = detail::unicode_LB_fit_lines(opportunities, widths, maximum_line_width);
730 hi_axiom(detail::unicode_LB_width_check(widths, r, maximum_line_width));
731 return r;
732}
733
734
735} // namespace hi::inline v1
Utilities to assert and bound check.
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
Miscellaneous math functions.
constexpr float unicode_LB_width(std::vector< float >::const_iterator first, std::vector< float >::const_iterator last) noexcept
Calculate the width of a line.
Definition unicode_line_break.hpp:435
constexpr std::vector< size_t > unicode_LB_mandatory_lines(unicode_break_vector const &opportunities) noexcept
Get the length of each line when broken with mandatory breaks.
Definition unicode_line_break.hpp:493
constexpr std::vector< size_t > unicode_LB_optional_lines(unicode_break_vector const &opportunities) noexcept
Get the length of each line when broken with mandatory and optional breaks.
Definition unicode_line_break.hpp:513
constexpr std::pair< float, std::vector< size_t > > unicode_LB_minimum_width(unicode_break_vector const &opportunities, std::vector< float > const &char_widths)
Get the minimum width of the text.
Definition unicode_line_break.hpp:664
constexpr std::pair< float, std::vector< size_t > > unicode_LB_maximum_width(unicode_break_vector const &opportunities, std::vector< float > const &char_widths)
Get the maximum width of the text.
Definition unicode_line_break.hpp:648
constexpr std::vector< size_t > unicode_LB_fit_lines(unicode_break_vector const &opportunities, std::vector< float > const &widths, float maximum_line_width) noexcept
Get the length of each line when broken after folding text to a maximum width.
Definition unicode_line_break.hpp:610
constexpr bool unicode_LB_width_check(std::vector< float > const &widths, std::vector< size_t > const &lengths, float maximum_line_width) noexcept
Check if all the lines in the text fit the maximum width.
Definition unicode_line_break.hpp:477
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
unicode_line_break_class
Unicode line break class.
Definition unicode_line_break.hpp:36
unicode_break_vector unicode_line_break(It first, ItEnd last, DescriptionFunc const &description_func) noexcept
The unicode line break algorithm UAX #14.
Definition unicode_line_break.hpp:699
@ AL
Right-to-Left Arabic.
Combined unicode_line_break_class and unicode_line_break_opportunity.
Definition unicode_line_break.hpp:98
T accumulate(T... args)
T begin(T... args)
T distance(T... args)
T find_if(T... args)
T move(T... args)
T next(T... args)
T push_back(T... args)
T reserve(T... args)