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