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