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
17namespace 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