summaryrefslogtreecommitdiff
path: root/src/emojis.cpp
blob: dc283100b9558ebe8e15bbe5163f624f7e0f0036 (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "emojis.hpp"
#include <sstream>
#include <utility>
#include <cstdio>

#ifdef ABADDON_IS_BIG_ENDIAN
/* Allows processing emojis.bin correctly on big-endian systems. */
int emojis_int32_correct_endian(int little_endian_in) {
    /* this does the same thing as __bswap_32() but can be done without
       non-standard headers. */
    return ((little_endian_in >> 24) & 0xff) |      // move byte 3 to byte 0
           ((little_endian_in << 8) & 0xff0000) |   // move byte 1 to byte 2
           ((little_endian_in >> 8) & 0xff00) |     // move byte 2 to byte 1
           ((little_endian_in << 24) & 0xff000000); // byte 0 to byte 3
}
#else
int emojis_int32_correct_endian(int little_endian_in) {
    return little_endian_in;
}
#endif

EmojiResource::EmojiResource(std::string filepath)
    : m_filepath(std::move(filepath)) {}

bool EmojiResource::Load() {
    m_fp = std::fopen(m_filepath.c_str(), "rb");
    if (m_fp == nullptr) return false;

    int index_offset;
    std::fread(&index_offset, 4, 1, m_fp);
    index_offset = emojis_int32_correct_endian(index_offset);
    std::fseek(m_fp, index_offset, SEEK_SET);

    int emojis_count;
    std::fread(&emojis_count, 4, 1, m_fp);
    emojis_count = emojis_int32_correct_endian(emojis_count);
    for (int i = 0; i < emojis_count; i++) {
        std::vector<std::string> shortcodes;

        int shortcodes_count;
        std::fread(&shortcodes_count, 4, 1, m_fp);
        shortcodes_count = emojis_int32_correct_endian(shortcodes_count);
        for (int j = 0; j < shortcodes_count; j++) {
            int shortcode_length;
            std::fread(&shortcode_length, 4, 1, m_fp);
            shortcode_length = emojis_int32_correct_endian(shortcode_length);
            std::string shortcode(shortcode_length, '\0');
            std::fread(shortcode.data(), shortcode_length, 1, m_fp);
            shortcodes.push_back(std::move(shortcode));
        }

        int surrogates_count;
        std::fread(&surrogates_count, 4, 1, m_fp);
        surrogates_count = emojis_int32_correct_endian(surrogates_count);
        std::string surrogates(surrogates_count, '\0');
        std::fread(surrogates.data(), surrogates_count, 1, m_fp);
        m_patterns.emplace_back(surrogates);

        int data_size, data_offset;
        std::fread(&data_size, 4, 1, m_fp);
        data_size = emojis_int32_correct_endian(data_size);
        std::fread(&data_offset, 4, 1, m_fp);
        data_offset = emojis_int32_correct_endian(data_offset);
        m_index[surrogates] = { data_offset, data_size };

        for (const auto &shortcode : shortcodes)
            m_shortcode_index[shortcode] = surrogates;

        m_pattern_shortcode_index[surrogates] = std::move(shortcodes);
    }

    std::sort(m_patterns.begin(), m_patterns.end(), [](const Glib::ustring &a, const Glib::ustring &b) {
        return a.size() > b.size();
    });
    return true;
}

Glib::RefPtr<Gdk::Pixbuf> EmojiResource::GetPixBuf(const Glib::ustring &pattern) {
    const auto it = m_index.find(pattern);
    if (it == m_index.end()) return {};
    const int pos = it->second.first;
    const int len = it->second.second;
    std::fseek(m_fp, pos, SEEK_SET);
    std::vector<uint8_t> data(len);
    std::fread(data.data(), len, 1, m_fp);
    auto loader = Gdk::PixbufLoader::create();
    loader->write(static_cast<const guint8 *>(data.data()), data.size());
    loader->close();
    return loader->get_pixbuf();
}

void EmojiResource::ReplaceEmojis(Glib::RefPtr<Gtk::TextBuffer> buf, int size) {
    auto get_text = [&]() -> auto {
        Gtk::TextBuffer::iterator a, b;
        buf->get_bounds(a, b);
        return buf->get_slice(a, b, true);
    };
    auto text = get_text();

    int searchpos;
    for (const auto &pattern : m_patterns) {
        searchpos = 0;
        Glib::RefPtr<Gdk::Pixbuf> pixbuf;
        while (true) {
            size_t r = text.find(pattern, searchpos);
            if (r == Glib::ustring::npos) break;
            if (!pixbuf) {
                pixbuf = GetPixBuf(pattern);
                if (pixbuf)
                    pixbuf = pixbuf->scale_simple(size, size, Gdk::INTERP_BILINEAR);
                else
                    break;
            }
            searchpos = static_cast<int>(r + pattern.size());

            const auto start_it = buf->get_iter_at_offset(static_cast<int>(r));
            const auto end_it = buf->get_iter_at_offset(static_cast<int>(r + pattern.size()));

            auto it = buf->erase(start_it, end_it);
            buf->insert_pixbuf(it, pixbuf);

            int alen = static_cast<int>(text.size());
            text = get_text();
            int blen = static_cast<int>(text.size());
            searchpos -= (alen - blen);
        }
    }
}

std::string EmojiResource::GetShortCodeForPattern(const Glib::ustring &pattern) {
    auto it = m_pattern_shortcode_index.find(pattern);
    if (it != m_pattern_shortcode_index.end())
        return it->second.front();
    return "";
}

const std::map<std::string, std::string> &EmojiResource::GetShortCodes() const {
    return m_shortcode_index;
}