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 "smufl.h"
23 :
24 : #include "io/file.h"
25 : #include "serialization/json.h"
26 :
27 : #include "types/symnames.h"
28 :
29 : #include "dom/mscore.h"
30 :
31 : #include "log.h"
32 :
33 : using namespace mu;
34 : using namespace muse;
35 : using namespace muse::io;
36 : using namespace mu::engraving;
37 :
38 : std::array<Smufl::Code, size_t(SymId::lastSym) + 1> Smufl::s_symIdCodes { { } };
39 :
40 391552 : const Smufl::Code Smufl::code(SymId id)
41 : {
42 391552 : return s_symIdCodes.at(static_cast<size_t>(id));
43 : }
44 :
45 43548 : char32_t Smufl::smuflCode(SymId id)
46 : {
47 43548 : return s_symIdCodes.at(static_cast<size_t>(id)).smuflCode;
48 : }
49 :
50 16 : bool Smufl::init()
51 : {
52 16 : bool ok = loadGlyphNamesJson();
53 :
54 16 : return ok;
55 : }
56 :
57 16 : bool Smufl::loadGlyphNamesJson()
58 : {
59 16 : File file(":/fonts/smufl/glyphnames.json");
60 16 : if (!file.open(IODevice::ReadOnly)) {
61 0 : LOGE() << "could not open glyph names JSON file.";
62 0 : return false;
63 : }
64 :
65 16 : std::string error;
66 16 : JsonObject glyphNamesJson = JsonDocument::fromJson(file.readAll(), &error).rootObject();
67 16 : file.close();
68 :
69 16 : if (!error.empty()) {
70 0 : LOGE() << "JSON parse error in glyph names file: " << error;
71 0 : return false;
72 : }
73 :
74 16 : IF_ASSERT_FAILED(!glyphNamesJson.empty()) {
75 0 : LOGE() << "Could not read glyph names JSON";
76 0 : return false;
77 : }
78 :
79 97920 : for (size_t i = 0; i < s_symIdCodes.size(); ++i) {
80 48944 : SymId sym = static_cast<SymId>(i);
81 48944 : if (sym == SymId::noSym || sym == SymId::lastSym) {
82 2032 : continue;
83 : }
84 :
85 48912 : std::string name(SymNames::nameForSymId(sym).ascii());
86 48912 : JsonObject symObj = glyphNamesJson.value(name).toObject();
87 48912 : if (!symObj.isValid()) {
88 2000 : continue;
89 : }
90 :
91 : bool ok;
92 140736 : char32_t code = symObj.value("codepoint").toString().mid(2).toUInt(&ok, 16);
93 46912 : if (ok) {
94 46912 : s_symIdCodes[i].smuflCode = code;
95 0 : } else if (MScore::debugMode) {
96 0 : LOGD() << "could not read codepoint for glyph " << name;
97 : }
98 :
99 140736 : char32_t alernativeCode = symObj.value("alternateCodepoint").toString().mid(2).toUInt(&ok, 16);
100 46912 : if (ok) {
101 3552 : s_symIdCodes[i].musicSymBlockCode = alernativeCode;
102 43360 : } else if (MScore::debugMode) {
103 0 : LOGD() << "could not read alternate codepoint for glyph " << name;
104 : }
105 50912 : }
106 16 : return true;
107 16 : }
108 :
109 0 : const std::map<String, std::vector<SymId> >& Smufl::smuflRanges()
110 : {
111 0 : static std::map<String, std::vector<SymId> > ranges = readSmuflRangesJson();
112 0 : return ranges;
113 : }
114 :
115 0 : std::map<String, std::vector<SymId> > Smufl::readSmuflRangesJson()
116 : {
117 0 : std::map<String, std::vector<SymId> > ranges;
118 0 : std::vector<SymId> allSymbols;
119 :
120 0 : File fi(":/fonts/smufl/ranges.json");
121 0 : if (!fi.open(IODevice::ReadOnly)) {
122 0 : LOGE() << "failed open: " << fi.filePath();
123 0 : return ranges;
124 : }
125 :
126 0 : std::string error;
127 0 : JsonObject o = JsonDocument::fromJson(fi.readAll(), &error).rootObject();
128 0 : if (!error.empty()) {
129 0 : LOGE() << "failed parse, err: " << error << ", file: " << fi.filePath();
130 0 : return ranges;
131 : }
132 :
133 0 : for (const std::string& s : o.keys()) {
134 0 : const JsonObject range = o.value(s).toObject();
135 0 : const String desc = range.value("description").toString();
136 0 : const JsonArray glyphs = range.value("glyphs").toArray();
137 :
138 0 : if (glyphs.empty()) {
139 0 : continue;
140 : }
141 0 : std::vector<SymId> rangeSymbols;
142 0 : for (size_t i = 0; i < glyphs.size(); ++i) {
143 0 : String symName = glyphs.at(i).toString();
144 0 : SymId symId = SymNames::symIdByName(symName);
145 0 : if (symId != SymId::noSym) {
146 0 : rangeSymbols.push_back(symId);
147 0 : allSymbols.push_back(symId);
148 : }
149 0 : }
150 0 : ranges.insert({ desc, rangeSymbols });
151 0 : }
152 :
153 0 : ranges.insert({ String::fromAscii(SMUFL_ALL_SYMBOLS), allSymbols }); // TODO: make translatable as well as ranges.json
154 :
155 0 : return ranges;
156 0 : }
|