1 | // LAF FreeType Wrapper |
2 | // Copyright (c) 2020-2022 Igara Studio S.A. |
3 | // Copyright (c) 2017 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #ifndef FT_HB_SHAPER_H_INCLUDED |
9 | #define FT_HB_SHAPER_H_INCLUDED |
10 | #pragma once |
11 | |
12 | #include "base/utf8_decode.h" |
13 | #include "ft/hb_face.h" |
14 | |
15 | #include <vector> |
16 | |
17 | namespace ft { |
18 | |
19 | template<typename HBFace> |
20 | class HBShaper { |
21 | public: |
22 | |
23 | HBShaper(HBFace& face, const std::string& str) |
24 | : m_face(face) { |
25 | base::utf8_decode decode(str); |
26 | if (decode.is_end()) |
27 | return; |
28 | |
29 | hb_buffer_t* buf = hb_buffer_create(); |
30 | hb_buffer_t* chrBuf = hb_buffer_create(); |
31 | hb_script_t script = HB_SCRIPT_UNKNOWN; |
32 | |
33 | const auto begin = str.begin(); |
34 | while (true) { |
35 | const auto pos = decode.pos(); |
36 | const int chr = decode.next(); |
37 | if (!chr) |
38 | break; |
39 | |
40 | // Get the script of the next character in *it |
41 | hb_buffer_set_content_type(chrBuf, HB_BUFFER_CONTENT_TYPE_UNICODE); |
42 | hb_buffer_add(chrBuf, chr, 0); |
43 | hb_buffer_guess_segment_properties(chrBuf); |
44 | hb_script_t newScript = hb_buffer_get_script(chrBuf); |
45 | hb_buffer_reset(chrBuf); |
46 | |
47 | if (newScript && script != newScript) { |
48 | addBuffer(buf, script); |
49 | hb_buffer_reset(buf); |
50 | script = newScript; |
51 | } |
52 | |
53 | hb_buffer_add(buf, chr, pos - begin); |
54 | } |
55 | addBuffer(buf, script); |
56 | |
57 | hb_buffer_destroy(buf); |
58 | hb_buffer_destroy(chrBuf); |
59 | } |
60 | |
61 | int next() { |
62 | if (++m_index < m_glyphCount) |
63 | return m_glyphInfo[m_index].codepoint; |
64 | else |
65 | return 0; |
66 | } |
67 | |
68 | int unicodeChar() const { |
69 | return m_glyphInfo[m_index].codepoint; |
70 | } |
71 | |
72 | int charIndex() const { |
73 | return m_glyphInfo[m_index].cluster; |
74 | } |
75 | |
76 | unsigned int glyphIndex() const { |
77 | return m_glyphInfo[m_index].codepoint; |
78 | } |
79 | |
80 | void glyphOffsetXY(Glyph* glyph) { |
81 | glyph->x += m_glyphPos[m_index].x_offset / 64.0; |
82 | glyph->y += m_glyphPos[m_index].y_offset / 64.0; |
83 | } |
84 | |
85 | void glyphAdvanceXY(const Glyph* glyph, double& x, double& y) { |
86 | x += m_glyphPos[m_index].x_advance / 64.0; |
87 | y += m_glyphPos[m_index].y_advance / 64.0; |
88 | } |
89 | |
90 | private: |
91 | void addBuffer(hb_buffer_t* buf, hb_script_t script) { |
92 | if (hb_buffer_get_length(buf) == 0) |
93 | return; |
94 | |
95 | // Just in case we're compiling with an old harfbuzz version |
96 | #ifdef HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS |
97 | hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); |
98 | #endif |
99 | hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE); |
100 | hb_buffer_set_script(buf, script); |
101 | hb_buffer_set_direction(buf, hb_script_get_horizontal_direction(script)); |
102 | |
103 | hb_shape(m_face.font(), buf, nullptr, 0); |
104 | |
105 | unsigned int count; |
106 | auto info = hb_buffer_get_glyph_infos(buf, &count); |
107 | auto pos = hb_buffer_get_glyph_positions(buf, &count); |
108 | |
109 | m_glyphCount += count; |
110 | const unsigned int start = m_glyphInfo.size(); |
111 | m_glyphInfo.resize(m_glyphCount); |
112 | m_glyphPos.resize(m_glyphCount); |
113 | for (unsigned int i=0; i<count; ++i) { |
114 | m_glyphInfo[start+i] = info[i]; |
115 | m_glyphPos[start+i] = pos[i]; |
116 | } |
117 | } |
118 | |
119 | HBFace& m_face; |
120 | std::vector<hb_glyph_info_t> m_glyphInfo; |
121 | std::vector<hb_glyph_position_t> m_glyphPos; |
122 | int m_glyphCount = 0; |
123 | int m_index = -1; |
124 | }; |
125 | |
126 | } // namespace ft |
127 | |
128 | #endif |
129 | |