Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "htmlparser.h"
23 :
24 : #include <string_view>
25 :
26 : namespace mu::engraving {
27 49 : muse::String HtmlParser::parse(const muse::String& in_html)
28 : {
29 49 : if (in_html.isEmpty()) {
30 0 : return in_html;
31 : }
32 :
33 49 : std::string html = in_html.toStdString();
34 :
35 : //! NOTE Get body
36 49 : auto body_b = html.find("<body");
37 49 : auto body_e = html.find_last_of("</body>");
38 49 : if (body_b == std::string::npos || body_e == std::string::npos) {
39 0 : return in_html;
40 : }
41 49 : std::string body = html.substr(body_b, body_e - body_b);
42 :
43 49 : std::vector<std::string> blocks;
44 :
45 : //! NOTE Split blocks
46 49 : std::string::size_type p_b = 0;
47 49 : std::string::size_type p_e = 0;
48 : while (true) {
49 98 : p_b = body.find("<p", p_e);
50 98 : p_e = body.find("/p>", p_b);
51 98 : if (p_b == std::string::npos || p_e == std::string::npos) {
52 : break;
53 : }
54 :
55 49 : std::string block = body.substr(p_b, p_e - p_b);
56 49 : blocks.push_back(block);
57 49 : }
58 :
59 49 : if (blocks.empty()) {
60 0 : blocks.push_back(body);
61 : }
62 :
63 : //! NOTE Format blocks
64 49 : auto extractText = [](const std::string& block) {
65 49 : std::string text;
66 49 : bool isTag = false;
67 7494 : for (const char& c : block) {
68 7445 : if (c == '<') {
69 145 : isTag = true;
70 7300 : } else if (c == '>') {
71 96 : isTag = false;
72 7204 : } else if (!isTag) {
73 279 : text += c;
74 : }
75 : }
76 49 : return text;
77 0 : };
78 :
79 49 : auto extractFont = [](const std::string& block) {
80 49 : auto fontsize_b = block.find("font-size");
81 49 : if (fontsize_b != std::string::npos) {
82 16 : std::string fontSize;
83 16 : bool started = false;
84 206 : for (auto i = fontsize_b; i < block.size(); ++i) {
85 206 : const char& c = block.at(i);
86 206 : if (strchr(".0123456789", c) != nullptr) {
87 30 : started = true;
88 30 : fontSize += c;
89 176 : } else if (started) {
90 16 : break;
91 : }
92 : }
93 :
94 48 : return std::string("<font size=\"") + fontSize + std::string("\"/>");
95 16 : }
96 33 : return std::string();
97 : };
98 :
99 49 : auto formatRichText = [extractText, extractFont](const std::string& block) {
100 49 : std::string text = extractText(block);
101 49 : std::string font = extractFont(block);
102 49 : if (!font.empty()) {
103 16 : text = font + text;
104 : }
105 49 : return text;
106 49 : };
107 :
108 : //! NOTE Format rich text from blocks
109 49 : std::string text;
110 98 : for (const std::string& block : blocks) {
111 49 : if (!text.empty()) {
112 0 : text += "\n";
113 : }
114 :
115 49 : text += formatRichText(block);
116 : }
117 :
118 686 : auto replaceSym = [](std::string& str, int cc, std::string_view sym) {
119 686 : std::string code;
120 686 : code.resize(3);
121 686 : code[2] = static_cast<char>(cc);
122 686 : code[1] = static_cast<char>(cc >> 8);
123 686 : code[0] = static_cast<char>(cc >> 16);
124 :
125 691 : for (auto pos = str.find(code); pos != std::string::npos; pos = str.find(code, pos + sym.size())) {
126 5 : str.replace(pos, 3, sym);
127 : }
128 686 : };
129 :
130 : //! NOTE replace utf8 code /*utf16 code*/ on sym
131 49 : replaceSym(text, 0xee848e /*0xe10e*/, "<sym>accidentalNatural</sym>"); //natural
132 49 : replaceSym(text, 0xee848c /*0xe10c*/, "<sym>accidentalSharp</sym>"); // sharp
133 49 : replaceSym(text, 0xee848d /*0xe10d*/, "<sym>accidentalFlat</sym>"); // flat
134 49 : replaceSym(text, 0xee8484 /*0xe104*/, "<sym>metNoteHalfUp</sym>"); // note2_Sym
135 49 : replaceSym(text, 0xee8485 /*0xe105*/, "<sym>metNoteQuarterUp</sym>"); // note4_Sym
136 49 : replaceSym(text, 0xee8486 /*0xe106*/, "<sym>metNote8thUp</sym>"); // note8_Sym
137 49 : replaceSym(text, 0xee8487 /*0xe107*/, "<sym>metNote16thUp</sym>"); // note16_Sym
138 49 : replaceSym(text, 0xee8488 /*0xe108*/, "<sym>metNote32ndUp</sym>"); // note32_Sym
139 49 : replaceSym(text, 0xee8489 /*0xe109*/, "<sym>metNote64thUp</sym>"); // note64_Sym
140 49 : replaceSym(text, 0xee848a /*0xe10a*/, "<sym>metAugmentationDot</sym>"); // dot
141 49 : replaceSym(text, 0xee848b /*0xe10b*/, "<sym>metAugmentationDot</sym><sym>space</sym><sym>metAugmentationDot</sym>"); // dotdot
142 49 : replaceSym(text, 0xee85a7 /*0xe167*/, "<sym>segno</sym>"); // segno
143 49 : replaceSym(text, 0xee85a8 /*0xe168*/, "<sym>coda</sym>"); // coda
144 49 : replaceSym(text, 0xee85a9 /*0xe169*/, "<sym>codaSquare</sym>"); // varcoda
145 :
146 49 : return muse::String::fromStdString(text);
147 49 : }
148 : }
|