summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibGfx/TextDirection.h
blob: dc5ef5b184ac473751aecf70b61966a8d381fe6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
 * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/Array.h>
#include <AK/Utf32View.h>
#include <AK/Vector.h>

namespace Gfx {

enum class BidirectionalClass {
    STRONG_LTR,
    STRONG_RTL,
    WEAK_NUMBERS,
    WEAK_SEPARATORS,
    NEUTRAL,
};

extern const Array<BidirectionalClass, 0x1F000> char_bidi_class_lookup_table;

constexpr BidirectionalClass get_char_bidi_class(u32 ch)
{
    if (ch >= char_bidi_class_lookup_table.size())
        return BidirectionalClass::STRONG_LTR;
    return char_bidi_class_lookup_table[ch];
}

// FIXME: These should be parsed from the official BidiMirroring.txt that specifies the mirroring character for each character (this function doesn't take into account a large amount of characters)
constexpr u32 get_mirror_char(u32 ch)
{
    if (ch == 0x28)
        return 0x29;
    if (ch == 0x29)
        return 0x28;
    if (ch == 0x3C)
        return 0x3E;
    if (ch == 0x3E)
        return 0x3C;
    if (ch == 0x5B)
        return 0x5D;
    if (ch == 0x7B)
        return 0x7D;
    if (ch == 0x7D)
        return 0x7B;
    if (ch == 0xAB)
        return 0xBB;
    if (ch == 0xBB)
        return 0xAB;
    if (ch == 0x2039)
        return 0x203A;
    if (ch == 0x203A)
        return 0x2039;
    return ch;
}

enum class TextDirection {
    LTR,
    RTL,
};

constexpr TextDirection bidi_class_to_direction(BidirectionalClass class_)
{
    VERIFY(class_ != BidirectionalClass::NEUTRAL);
    if (class_ == BidirectionalClass::STRONG_LTR || class_ == BidirectionalClass::WEAK_NUMBERS || class_ == BidirectionalClass::WEAK_SEPARATORS)
        return TextDirection::LTR;
    return TextDirection::RTL;
}

// Assumes the text has a homogeneous direction
template<typename TextType>
constexpr TextDirection get_text_direction(TextType text)
{
    for (u32 code_point : text) {
        auto char_direction = get_char_bidi_class(code_point);
        if (char_direction != BidirectionalClass::NEUTRAL)
            return bidi_class_to_direction(char_direction);
    }
    return TextDirection::LTR;
}

class DirectionalRun {
public:
    DirectionalRun(Vector<u32> code_points, u8 embedding_level)
        : m_code_points(move(code_points))
        , m_embedding_level(embedding_level)
    {
    }

    [[nodiscard]] Utf32View text() const { return { m_code_points.data(), m_code_points.size() }; }
    [[nodiscard]] u8 embedding_level() const { return m_embedding_level; }
    [[nodiscard]] TextDirection direction() const { return (m_embedding_level % 2) == 0 ? TextDirection::LTR : TextDirection::RTL; }

    Vector<u32>& code_points() { return m_code_points; }

private:
    Vector<u32> m_code_points;
    u8 m_embedding_level;
};

}