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
100[[nodiscard]] constexpr float
101unicode_line_break_width(std::vector<float>::const_iterator first, std::vector<float>::const_iterator last) noexcept
102{
103 if (first == last) {
104 return 0.0f;
105 }
106
107 auto rfirst = std::make_reverse_iterator(last);
108 auto rlast = std::make_reverse_iterator(first);
109
110 auto it = std::find_if(rfirst, rlast, [](hilet &width) {
111 return width >= 0.0;
112 });
113 return std::accumulate(it, rlast, 0.0f, [](float acc, hilet &width) {
114 return acc + abs(width);
115 });
116}
117
118namespace detail {
119
123 unicode_line_break_class original_class = unicode_line_break_class::XX;
124 unicode_line_break_class current_class = unicode_line_break_class::XX;
125 bool is_extended_pictographic = false;
126 bool is_Cn = false;
127 unicode_east_asian_width east_asian_width = unicode_east_asian_width::A;
128
129 constexpr unicode_line_break_info() noexcept = default;
130 constexpr unicode_line_break_info(unicode_line_break_info const &) noexcept = default;
131 constexpr unicode_line_break_info(unicode_line_break_info &&) noexcept = default;
132 constexpr unicode_line_break_info &operator=(unicode_line_break_info const &) noexcept = default;
133 constexpr unicode_line_break_info &operator=(unicode_line_break_info &&) noexcept = default;
134
135 constexpr explicit unicode_line_break_info(
136 unicode_line_break_class break_class,
137 bool is_Cn,
138 bool is_extended_pictographic,
139 unicode_east_asian_width east_asian_width) noexcept :
140 original_class(break_class),
141 current_class(break_class),
142 is_Cn(is_Cn),
143 is_extended_pictographic(is_extended_pictographic),
144 east_asian_width(east_asian_width)
145 {
146 }
147
148 constexpr explicit operator unicode_line_break_class() const noexcept
149 {
150 return current_class;
151 }
152
153 constexpr unicode_line_break_info &operator|=(unicode_line_break_class rhs) noexcept
154 {
155 current_class = rhs;
156 return *this;
157 }
158
159 [[nodiscard]] constexpr bool operator==(unicode_line_break_class rhs) const noexcept
160 {
161 return current_class == rhs;
162 }
163
164 [[nodiscard]] constexpr bool operator==(unicode_east_asian_width rhs) const noexcept
165 {
166 return east_asian_width == rhs;
167 }
168};
169
171using unicode_line_break_info_iterator = unicode_line_break_info_vector::iterator;
172using unicode_line_break_info_const_iterator = unicode_line_break_info_vector::const_iterator;
173
174template<typename It, typename ItEnd, typename DescriptionFunc>
175[[nodiscard]] constexpr std::vector<unicode_line_break_info>
176unicode_LB1(It first, ItEnd last, DescriptionFunc const &description_func) noexcept
177{
179 r.reserve(std::distance(first, last));
180
181 for (auto it = first; it != last; ++it) {
182 hilet &description = description_func(*it);
183 hilet break_class = description.line_break_class();
184 hilet general_category = description.general_category();
185
186 hilet resolved_break_class = [&]() {
187 switch (break_class) {
188 using enum unicode_line_break_class;
189 case AI:
190 case SG:
191 case XX: return AL;
192 case CJ: return NS;
193 case SA: return is_Mn_or_Mc(general_category) ? CM : AL;
194 default: return break_class;
195 }
196 }();
197
198 r.emplace_back(
199 resolved_break_class,
200 general_category == unicode_general_category::Cn,
201 description.grapheme_cluster_break() == unicode_grapheme_cluster_break::Extended_Pictographic,
202 description.east_asian_width());
203 }
204
205 return r;
206}
207
208[[nodiscard]] constexpr void unicode_LB2_3(unicode_break_vector &opportunities) noexcept
209{
210 hi_axiom(not opportunities.empty());
211 // LB2
212 opportunities.front() = unicode_break_opportunity::no;
213 // LB3
214 opportunities.back() = unicode_break_opportunity::mandatory;
215}
216
217template<typename MatchFunc>
218constexpr void unicode_LB_walk(
219 unicode_break_vector &opportunities,
221 MatchFunc match_func) noexcept
222{
223 using enum unicode_line_break_class;
224
225 if (infos.empty()) {
226 return;
227 }
228
229 auto cur = infos.begin();
230 hilet last = infos.end() - 1;
231 hilet last2 = infos.end();
232 auto opportunity = opportunities.begin() + 1;
233
234 auto cur_sp_class = XX;
235 auto cur_nu_class = XX;
236 auto prev_class = XX;
237 auto num_ri = 0_uz;
238 while (cur != last) {
239 hilet next = cur + 1;
240 hilet cur_class = unicode_line_break_class{*cur};
241 hilet next2_class = cur + 2 == last2 ? XX : unicode_line_break_class{*(cur + 2)};
242
243 // Keep track of classes followed by zero or more SP.
244 if (cur_class != SP) {
245 cur_sp_class = cur_class;
246 }
247
248 // Keep track of a "NU (NU|SY|IS)*" and "NU (NU|SY|IS)* (CL|CP)?".
249 if (cur_nu_class == CL) {
250 // Only a single CL|CP class may be at the end, then the number is closed.
251 cur_nu_class = XX;
252 } else if (cur_nu_class == NU) {
253 if (cur_class == CL or cur_class == CP) {
254 cur_nu_class = CL;
255 } else if (cur_class != NU and cur_class != SY and cur_class != IS) {
256 cur_nu_class = XX;
257 }
258 } else if (cur_class == NU) {
259 cur_nu_class = NU;
260 }
261
262 // Keep track of consecutive RI, but only count the actual RIs.
263 if (cur->original_class == RI) {
264 ++num_ri;
265 } else if (*cur != RI) {
266 num_ri = 0;
267 }
268
269 if (*opportunity == unicode_break_opportunity::unassigned) {
270 *opportunity = match_func(prev_class, cur, next, next2_class, cur_sp_class, cur_nu_class, num_ri);
271 }
272
273 prev_class = cur_class;
274 cur = next;
275 ++opportunity;
276 }
277}
278
279constexpr void unicode_LB4_8a(unicode_break_vector &opportunities, std::vector<unicode_line_break_info> const &infos) noexcept
280{
281 unicode_LB_walk(
282 opportunities, infos, [](hilet prev, hilet cur, hilet next, hilet next2, hilet cur_sp, hilet cur_nu, hilet num_ri) {
283 using enum unicode_break_opportunity;
284 using enum unicode_line_break_class;
285 if (*cur == BK) {
286 return mandatory; // LB4: 4.0
287 } else if (*cur == CR and *next == LF) {
288 return no; // LB5: 5.01
289 } else if (*cur == CR or *cur == LF or *cur == NL) {
290 return mandatory; // LB5: 5.02, 5.03, 5.04
291 } else if (*next == BK or *next == CR or *next == LF or *next == NL) {
292 return no; // LB6: 6.0
293 } else if (*next == SP or *next == ZW) {
294 return no; // LB7: 7.01, 7.02
295 } else if (cur_sp == ZW) {
296 return yes; // LB8: 8.0
297 } else if (*cur == ZWJ) {
298 return no; // LB8a: 8.1
299 } else {
300 return unassigned;
301 }
302 });
303}
304
305constexpr void unicode_LB9(unicode_break_vector &opportunities, std::vector<unicode_line_break_info> &infos) noexcept
306{
307 using enum unicode_line_break_class;
308 using enum unicode_break_opportunity;
309
310 if (infos.empty()) {
311 return;
312 }
313
314 auto cur = infos.begin();
315 hilet last = infos.end() - 1;
316 auto opportunity = opportunities.begin() + 1;
317
318 auto X = XX;
319 while (cur != last) {
320 hilet next = cur + 1;
321
322 if ((*cur == CM or *cur == ZWJ) and X != XX) {
323 // Treat all CM/ZWJ as X (if there is an X).
324 *cur |= X;
325 } else {
326 // Reset X on non-CM/ZWJ.
327 X = XX;
328 }
329
330 if ((*cur != BK and *cur != CR and *cur != LF and *cur != NL and *cur != SP and *cur != ZW) and
331 (*next == CM or *next == ZWJ)) {
332 // [^BK CR LF NL SP ZW] x [CM ZWJ]*
333 *opportunity = no;
334
335 if (X == XX) {
336 // The first character of [^BK CR LF NL SP ZW] x [CM ZWJ]* => X
337 X = static_cast<unicode_line_break_class>(*cur);
338 }
339 }
340
341 cur = next;
342 ++opportunity;
343 }
344}
345
346constexpr void unicode_LB10(std::vector<unicode_line_break_info> &infos) noexcept
347{
348 using enum unicode_line_break_class;
349
350 for (auto &x : infos) {
351 if (x == CM or x == ZWJ) {
352 x |= AL;
353 }
354 }
355}
356
357constexpr void unicode_LB11_31(unicode_break_vector &opportunities, std::vector<unicode_line_break_info> const &infos) noexcept
358{
359 unicode_LB_walk(
360 opportunities, infos, [&](hilet prev, hilet cur, hilet next, hilet next2, hilet cur_sp, hilet cur_nu, hilet num_ri) {
361 using enum unicode_break_opportunity;
362 using enum unicode_line_break_class;
363 using enum unicode_east_asian_width;
364
365 if (*cur == WJ or *next == WJ) {
366 return no; // LB11: 11.01, 11.02
367 } else if (*cur == GL) {
368 return no; // LB12: 12.0
369 } else if (*cur != SP and *cur != BA and *cur != HY and *next == GL) {
370 return no; // LB12a: 12.1
371 } else if (*next == CL or *next == CP or *next == EX or *next == IS or *next == SY) {
372 return no; // LB13: 13.0
373 } else if (cur_sp == OP) {
374 return no; // LB14: 14.0
375 } else if (cur_sp == QU and *next == OP) {
376 return no; // LB15: 15.0
377 } else if ((cur_sp == CL or cur_sp == CP) and *next == NS) {
378 return no; // LB16: 16.0
379 } else if (cur_sp == B2 and *next == B2) {
380 return no; // LB17: 17.0
381 } else if (*cur == SP) {
382 return yes; // LB18: 18.0
383 } else if (*cur == QU or *next == QU) {
384 return no; // LB19: 19.01, 19.02
385 } else if (*cur == CB or *next == CB) {
386 return yes; // LB20: 20.01, 20.02
387 } else if (*cur == BB or *next == BA or *next == HY or *next == NS) {
388 return no; // LB21: 21.01, 21.02, 21.03, 21.04
389 } else if (prev == HL and (*cur == HY or *cur == BA)) {
390 return no; // LB21a: 21.1
391 } else if (*cur == SY and *next == HL) {
392 return no; // LB21b: 21.2
393 } else if (*next == IN) {
394 return no; // LB22: 22.0
395 } else if ((*cur == AL or *cur == HL) and *next == NU) {
396 return no; // LB23: 23.02
397 } else if (*cur == NU and (*next == AL or *next == HL)) {
398 return no; // LB23: 23.03
399 } else if (*cur == PR and (*next == ID or *next == EB or *next == EM)) {
400 return no; // LB23a: 23.12
401 } else if ((*cur == ID or *cur == EB or *cur == EM) and *next == PO) {
402 return no; // LB23a: 23.13
403 } else if ((*cur == PR or *cur == PO) and (*next == AL or *next == HL)) {
404 return no; // LB24: 24.02
405 } else if ((*cur == AL or *cur == HL) and (*next == PR or *next == PO)) {
406 return no; // LB24: 24.03
407 } else if (
408 (*cur == PR or *cur == PO) and ((*next == OP and next2 == NU) or (*next == HY and next2 == NU) or *next == NU)) {
409 return no; // LB25: 25.01
410 } else if ((*cur == OP or *cur == HY) and *next == NU) {
411 return no; // LB25: 25.02
412 } else if (*cur == NU and (*next == NU or *next == SY or *next == IS)) {
413 return no; // LB25: 25.03
414 } else if (cur_nu == NU and (*next == NU or *next == SY or *next == IS or *next == CL or *next == CP)) {
415 return no; // LB25: 25.04
416 } else if ((cur_nu == NU or cur_nu == CL) and (*next == PO or *next == PR)) {
417 return no; // LB25: 25.05
418 } else if (*cur == JL and (*next == JL or *next == JV or *next == H2 or *next == H3)) {
419 return no; // LB26: 26.01
420 } else if ((*cur == JV or *cur == H2) and (*next == JV or *next == JT)) {
421 return no; // LB26: 26.02
422 } else if ((*cur == JT or *cur == H3) and *next == JT) {
423 return no; // LB26: 26.03
424 } else if ((*cur == JL or *cur == JV or *cur == JT or *cur == H2 or *cur == H3) and *next == PO) {
425 return no; // LB27: 27.01
426 } else if (*cur == PR and (*next == JL or *next == JV or *next == JT or *next == H2 or *next == H3)) {
427 return no; // LB27: 27.02
428 } else if ((*cur == AL or *cur == HL) and (*next == AL or *next == HL)) {
429 return no; // LB28: 28.0
430 } else if (*cur == IS and (*next == AL or *next == HL)) {
431 return no; // LB29: 29.0
432 } else if ((*cur == AL or *cur == HL or *cur == NU) and (*next == OP and *next != F and *next != W and *next != H)) {
433 return no; // LB30: 30.01
434 } else if ((*cur == CP and *cur != F and *cur != W and *cur != H) and (*next == AL or *next == HL or *next == NU)) {
435 return no; // LB30: 30.02
436 } else if (*cur == RI and *next == RI and (num_ri % 2) == 1) {
437 return no; // LB30a: 30.11, 30.12, 30.13
438 } else if (*cur == EB and *next == EM) {
439 return no; // LB30b: 30.21
440 } else if (cur->is_extended_pictographic and cur->is_Cn and *next == EM) {
441 return no; // LB30b: 30.22
442 } else {
443 return yes; // LB31: 999.0
444 }
445 });
446}
447
448[[nodiscard]] constexpr bool unicode_LB_width_check(
449 std::vector<float> const &widths,
450 std::vector<size_t> const &lengths,
451 float maximum_line_width) noexcept
452{
453 auto it = widths.begin();
454 for (auto length : lengths) {
455 if (unicode_line_break_width(it, it + length) > maximum_line_width) {
456 return false;
457 }
458 it += length;
459 }
460 return true;
461}
462
467[[nodiscard]] constexpr std::vector<size_t> unicode_LB_mandatory_lines(unicode_break_vector const &opportunities) noexcept
468{
469 auto r = std::vector<size_t>{};
470
471 auto length = 0_uz;
472 for (auto it = opportunities.begin() + 1; it != opportunities.end(); ++it) {
473 ++length;
474 if (*it == unicode_break_opportunity::mandatory) {
475 r.push_back(length);
476 length = 0_uz;
477 }
478 }
479
480 return r;
481}
482
483[[nodiscard]] constexpr unicode_break_const_iterator unicode_LB_fast_fit_line(
484 unicode_break_const_iterator opportunity_it,
485 std::vector<float>::const_iterator width_it,
486 float maximum_line_width) noexcept
487{
488 using enum unicode_break_opportunity;
489
490 auto width = 0.0f;
491 auto end_of_line = opportunity_it;
492 while (true) {
493 width += abs(*width_it);
494 if (width > maximum_line_width) {
495 // This character makes the width too long.
496 return end_of_line;
497
498 } else if (*opportunity_it == mandatory) {
499 // This character is an end-of-line.
500 return opportunity_it;
501
502 } else if (*opportunity_it == yes) {
503 // This character is a valid break opportunity.
504 end_of_line = opportunity_it;
505 }
506
507 ++opportunity_it;
508 ++width_it;
509 }
510 hi_unreachable();
511}
512
513[[nodiscard]] constexpr unicode_break_const_iterator unicode_LB_slow_fit_line(
514 unicode_break_const_iterator first,
515 unicode_break_const_iterator end_of_line,
516 std::vector<float>::const_iterator first_width,
517 float maximum_line_width) noexcept
518{
519 using enum unicode_break_opportunity;
520
521 // Carefully look forward for a break opportunity.
522 auto it = end_of_line;
523 while (true) {
524 hilet num_characters = std::distance(first, it + 1);
525 hilet line_width = unicode_line_break_width(first_width, first_width + num_characters);
526
527 if (line_width <= maximum_line_width) {
528 if (*it == mandatory) {
529 // The next mandatory break fits in the maximum width.
530 return it;
531
532 } else if (*it == yes) {
533 // The next break opportunity fits in the maximum width.
534 end_of_line = it;
535 }
536 } else {
537 // This break opportunity doesn't fit within the maximum width. Use the previous break opportunity.
538 return end_of_line;
539 }
540
541 ++it;
542 }
543 hi_unreachable();
544}
545
546[[nodiscard]] constexpr unicode_break_const_iterator
547unicode_LB_finish_fit_line(unicode_break_const_iterator first, unicode_break_const_iterator end_of_line) noexcept
548{
549 if (first == end_of_line) {
550 // We couldn't break the line to fit the maximum line width.
551 while (*end_of_line == unicode_break_opportunity::no) {
552 ++end_of_line;
553 }
554 }
555
556 // Return iterator past the end-of-line.
557 return end_of_line + 1;
558}
559
565 unicode_break_vector const &opportunities,
566 std::vector<float> const &widths,
567 float maximum_line_width) noexcept
568{
569 using enum unicode_break_opportunity;
570
571 auto r = std::vector<size_t>{};
572 if (widths.empty()) {
573 return r;
574 }
575
576 auto opportunity_it = opportunities.begin() + 1;
577 auto width_it = widths.begin();
578 while (width_it != widths.end()) {
579 // First quickly find when the line is too long.
580 auto opportunity_eol = unicode_LB_fast_fit_line(opportunity_it, width_it, maximum_line_width);
581 opportunity_eol = unicode_LB_slow_fit_line(opportunity_it, opportunity_eol, width_it, maximum_line_width);
582 opportunity_eol = unicode_LB_finish_fit_line(opportunity_it, opportunity_eol);
583
584 hilet num_characters = std::distance(opportunity_it, opportunity_eol);
585 r.push_back(num_characters);
586 opportunity_it += num_characters;
587 width_it += num_characters;
588 }
589
590 return r;
591}
592
593} // namespace detail
594
602template<typename It, typename ItEnd, typename DescriptionFunc>
603[[nodiscard]] inline unicode_break_vector unicode_line_break(It first, ItEnd last, DescriptionFunc const &description_func) noexcept
604{
605 auto size = narrow_cast<size_t>(std::distance(first, last));
606 auto r = unicode_break_vector{size + 1, unicode_break_opportunity::unassigned};
607
608 auto infos = detail::unicode_LB1(first, last, description_func);
609 detail::unicode_LB2_3(r);
610 detail::unicode_LB4_8a(r, infos);
611 detail::unicode_LB9(r, infos);
612 detail::unicode_LB10(infos);
613 detail::unicode_LB11_31(r, infos);
614 return r;
615}
616
624[[nodiscard]] constexpr std::vector<size_t>
625unicode_line_break(unicode_break_vector const &opportunities, std::vector<float> const &widths, float maximum_line_width)
626{
627 // See if the lines after mandatory breaks will fit the width and return.
628 auto r = detail::unicode_LB_mandatory_lines(opportunities);
629 if (detail::unicode_LB_width_check(widths, r, maximum_line_width)) {
630 return r;
631 }
632
633 r = detail::unicode_LB_fit_lines(opportunities, widths, maximum_line_width);
634 hi_axiom(detail::unicode_LB_width_check(widths, r, maximum_line_width));
635 return r;
636}
637
638} // 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 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:467
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 with mandatory breaks.
Definition unicode_line_break.hpp:564
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:603
@ AL
Right-to-Left Arabic.
constexpr float unicode_line_break_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:101
Combined unicode_line_break_class and unicode_line_break_opportunity.
Definition unicode_line_break.hpp:122
T accumulate(T... args)
T begin(T... args)
T distance(T... args)
T find_if(T... args)
T next(T... args)
T push_back(T... args)
T reserve(T... args)