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 "engravingfont.h"
23 :
24 : #include "engraving/dom/mscore.h"
25 : #include "serialization/json.h"
26 : #include "io/file.h"
27 : #include "draw/painter.h"
28 : #include "types/symnames.h"
29 :
30 : #include "dom/mscore.h"
31 : #include "../style/styledef.h"
32 :
33 : #include "smufl.h"
34 :
35 : #include "log.h"
36 :
37 : using namespace mu;
38 : using namespace muse;
39 : using namespace muse::io;
40 : using namespace muse::draw;
41 : using namespace mu::engraving;
42 :
43 : // =============================================
44 : // ScoreFont
45 : // =============================================
46 :
47 128 : EngravingFont::EngravingFont(const std::string& name, const std::string& family, const path_t& filePath, const path_t& metadataPath)
48 128 : : EngravingFont(name, family, std::make_shared<File>(filePath), std::make_shared<File>(metadataPath))
49 : {
50 128 : }
51 :
52 128 : EngravingFont::EngravingFont(const std::string& name, const std::string& family, std::shared_ptr<IODevice> font,
53 128 : std::shared_ptr<IODevice> metadata)
54 128 : : m_symbols(static_cast<size_t>(SymId::lastSym) + 1),
55 128 : m_name(name),
56 128 : m_family(family),
57 128 : m_fontDevice(std::move(font)),
58 384 : m_metadataDevice(std::move(metadata))
59 : {
60 128 : }
61 :
62 0 : EngravingFont::EngravingFont(const EngravingFont& other)
63 : {
64 0 : m_loaded = false;
65 0 : m_symbols = other.m_symbols;
66 0 : m_name = other.m_name;
67 0 : m_family = other.m_family;
68 0 : m_fontDevice = other.m_fontDevice;
69 0 : m_metadataDevice = other.m_metadataDevice;
70 0 : }
71 :
72 : // =============================================
73 : // Properties
74 : // =============================================
75 :
76 24028 : const std::string& EngravingFont::name() const
77 : {
78 24028 : return m_name;
79 : }
80 :
81 3466 : const std::string& EngravingFont::family() const
82 : {
83 3466 : return m_family;
84 : }
85 :
86 0 : std::unordered_map<Sid, PropertyValue> EngravingFont::engravingDefaults() const
87 : {
88 0 : return m_engravingDefaults;
89 : }
90 :
91 0 : double EngravingFont::textEnclosureThickness()
92 : {
93 0 : return m_textEnclosureThickness;
94 : }
95 :
96 : // =============================================
97 : // Load
98 : // =============================================
99 :
100 22663 : void EngravingFont::ensureLoad()
101 : {
102 22663 : if (m_loaded) {
103 22535 : return;
104 : }
105 :
106 128 : const String family = String::fromStdString(m_family);
107 128 : if (!m_fontDevice || -1 == fontProvider()->addSymbolFont(family, m_fontDevice)) {
108 0 : LOGE() << "fatal error: cannot load font: " << m_name;
109 0 : return;
110 : }
111 :
112 128 : m_font.setWeight(Font::Normal);
113 128 : m_font.setItalic(false);
114 128 : m_font.setFamily(String::fromStdString(m_family), Font::Type::MusicSymbol);
115 128 : m_font.setNoFontMerging(true);
116 128 : m_font.setHinting(Font::Hinting::PreferVerticalHinting);
117 :
118 128 : m_font.setPointSizeF(StyleDef::DEFAULT_SMUFL_POINT_SIZE());
119 :
120 391680 : for (size_t id = 0; id < m_symbols.size(); ++id) {
121 391552 : Smufl::Code code = Smufl::code(static_cast<SymId>(id));
122 391552 : if (!code.isValid()) {
123 16256 : continue;
124 : }
125 375296 : Sym& sym = m_symbols[id];
126 375296 : computeMetrics(sym, code);
127 : }
128 :
129 128 : if (!m_metadataDevice || !m_metadataDevice->open(IODevice::ReadOnly)) {
130 0 : LOGE() << "Failed to open glyph metadata of " << m_name;
131 0 : return;
132 : }
133 :
134 128 : m_metadataDevice->seek(0);
135 128 : const ByteArray metadata = m_metadataDevice->readAll();
136 128 : m_metadataDevice->close();
137 :
138 128 : std::string error;
139 128 : const JsonObject metadataJson = JsonDocument::fromJson(metadata, &error).rootObject();
140 128 : if (!error.empty()) {
141 0 : LOGE() << "Json parse error in glyph metadata of " << m_name << ", error: " << error;
142 0 : return;
143 : }
144 128 : if (!metadataJson.isValid()) {
145 0 : LOGE() << "No valid JSON object in glyph metadata of " << m_name;
146 0 : return;
147 : }
148 :
149 384 : loadGlyphsWithAnchors(metadataJson.value("glyphsWithAnchors").toObject());
150 128 : loadComposedGlyphs();
151 384 : loadStylisticAlternates(metadataJson.value("glyphsWithAlternates").toObject());
152 384 : loadEngravingDefaults(metadataJson.value("engravingDefaults").toObject());
153 :
154 128 : m_loaded = true;
155 128 : }
156 :
157 128 : void EngravingFont::loadComposedGlyphs()
158 : {
159 : static const struct ComposedGlyph {
160 : const SymId id;
161 : const SymIdList subSymbolIds;
162 : } composedGlyphs[] = {
163 : { SymId::ornamentPrallMordent, {
164 : SymId::ornamentZigZagLineNoRightEnd,
165 : SymId::ornamentZigZagLineNoRightEnd,
166 : SymId::ornamentMiddleVerticalStroke,
167 : SymId::ornamentZigZagLineWithRightEnd
168 : } },
169 : { SymId::ornamentUpPrall, {
170 : SymId::ornamentBottomLeftConcaveStroke,
171 : SymId::ornamentZigZagLineNoRightEnd,
172 : SymId::ornamentZigZagLineNoRightEnd,
173 : SymId::ornamentZigZagLineWithRightEnd
174 : } },
175 : { SymId::ornamentUpMordent, {
176 : SymId::ornamentBottomLeftConcaveStroke,
177 : SymId::ornamentZigZagLineNoRightEnd,
178 : SymId::ornamentZigZagLineNoRightEnd,
179 : SymId::ornamentMiddleVerticalStroke,
180 : SymId::ornamentZigZagLineWithRightEnd
181 : } },
182 : { SymId::ornamentPrallDown, {
183 : SymId::ornamentZigZagLineNoRightEnd,
184 : SymId::ornamentZigZagLineNoRightEnd,
185 : SymId::ornamentZigZagLineNoRightEnd,
186 : SymId::ornamentBottomRightConcaveStroke,
187 : } },
188 : { SymId::ornamentDownMordent, {
189 : SymId::ornamentLeftVerticalStroke,
190 : SymId::ornamentZigZagLineNoRightEnd,
191 : SymId::ornamentZigZagLineNoRightEnd,
192 : SymId::ornamentMiddleVerticalStroke,
193 : SymId::ornamentZigZagLineWithRightEnd
194 : } },
195 : { SymId::ornamentPrallUp, {
196 : SymId::ornamentZigZagLineNoRightEnd,
197 : SymId::ornamentZigZagLineNoRightEnd,
198 : SymId::ornamentZigZagLineNoRightEnd,
199 : SymId::ornamentTopRightConvexStroke,
200 : } },
201 : { SymId::ornamentLinePrall, {
202 : SymId::ornamentLeftVerticalStroke,
203 : SymId::ornamentZigZagLineNoRightEnd,
204 : SymId::ornamentZigZagLineNoRightEnd,
205 : SymId::ornamentZigZagLineWithRightEnd
206 : } }
207 160 : };
208 :
209 1024 : for (const ComposedGlyph& c : composedGlyphs) {
210 896 : Sym& sym = this->sym(c.id);
211 896 : if (!sym.isValid()) {
212 896 : sym.subSymbolIds = c.subSymbolIds;
213 896 : sym.bbox = bbox(c.subSymbolIds, 1.0);
214 : }
215 : }
216 128 : }
217 :
218 : static const struct GlyphWithAlternates {
219 : const std::string key;
220 : const std::string alternateKey;
221 : const SymId alternateSymId;
222 : } GLYPHS_WITH_ALTERNATES[] = {
223 : { std::string("4stringTabClef"),
224 : std::string("4stringTabClefSerif"),
225 : SymId::fourStringTabClefSerif
226 : },
227 : { std::string("6stringTabClef"),
228 : std::string("6stringTabClefSerif"),
229 : SymId::sixStringTabClefSerif
230 : },
231 : { std::string("cClef"),
232 : std::string("cClefFrench"),
233 : SymId::cClefFrench
234 : },
235 : { std::string("cClef"),
236 : std::string("cClefFrench20C"),
237 : SymId::cClefFrench20C
238 : },
239 : { std::string("fClef"),
240 : std::string("fClefFrench"),
241 : SymId::fClefFrench
242 : },
243 : { std::string("fClef"),
244 : std::string("fClef19thCentury"),
245 : SymId::fClef19thCentury
246 : },
247 : { std::string("noteheadBlack"),
248 : std::string("noteheadBlackOversized"),
249 : SymId::noteheadBlack
250 : },
251 : { std::string("noteheadHalf"),
252 : std::string("noteheadHalfOversized"),
253 : SymId::noteheadHalf
254 : },
255 : { std::string("noteheadWhole"),
256 : std::string("noteheadWholeOversized"),
257 : SymId::noteheadWhole
258 : },
259 : { std::string("noteheadDoubleWhole"),
260 : std::string("noteheadDoubleWholeOversized"),
261 : SymId::noteheadDoubleWhole
262 : },
263 : { std::string("noteheadDoubleWholeSquare"),
264 : std::string("noteheadDoubleWholeSquareOversized"),
265 : SymId::noteheadDoubleWholeSquare
266 : },
267 : { std::string("noteheadDoubleWhole"),
268 : std::string("noteheadDoubleWholeAlt"),
269 : SymId::noteheadDoubleWholeAlt
270 : },
271 : { std::string("brace"),
272 : std::string("braceSmall"),
273 : SymId::braceSmall
274 : },
275 : { std::string("brace"),
276 : std::string("braceLarge"),
277 : SymId::braceLarge
278 : },
279 : { std::string("brace"),
280 : std::string("braceLarger"),
281 : SymId::braceLarger
282 : },
283 : { std::string("flag1024thDown"),
284 : std::string("flag1024thDownStraight"),
285 : SymId::flag1024thDownStraight
286 : },
287 : { std::string("flag1024thUp"),
288 : std::string("flag1024thUpStraight"),
289 : SymId::flag1024thUpStraight
290 : },
291 : { std::string("flag128thDown"),
292 : std::string("flag128thDownStraight"),
293 : SymId::flag128thDownStraight
294 : },
295 : { std::string("flag128thUp"),
296 : std::string("flag128thUpStraight"),
297 : SymId::flag128thUpStraight
298 : },
299 : { std::string("flag16thDown"),
300 : std::string("flag16thDownStraight"),
301 : SymId::flag16thDownStraight
302 : },
303 : { std::string("flag16thUp"),
304 : std::string("flag16thUpStraight"),
305 : SymId::flag16thUpStraight
306 : },
307 : { std::string("flag256thDown"),
308 : std::string("flag256thDownStraight"),
309 : SymId::flag256thDownStraight
310 : },
311 : { std::string("flag256thUp"),
312 : std::string("flag256thUpStraight"),
313 : SymId::flag256thUpStraight
314 : },
315 : { std::string("flag32ndDown"),
316 : std::string("flag32ndDownStraight"),
317 : SymId::flag32ndDownStraight
318 : },
319 : { std::string("flag32ndUp"),
320 : std::string("flag32ndUpStraight"),
321 : SymId::flag32ndUpStraight
322 : },
323 : { std::string("flag512thDown"),
324 : std::string("flag512thDownStraight"),
325 : SymId::flag512thDownStraight
326 : },
327 : { std::string("flag512thUp"),
328 : std::string("flag512thUpStraight"),
329 : SymId::flag512thUpStraight
330 : },
331 : { std::string("flag64thDown"),
332 : std::string("flag64thDownStraight"),
333 : SymId::flag64thDownStraight
334 : },
335 : { std::string("flag64thUp"),
336 : std::string("flag64thUpStraight"),
337 : SymId::flag64thUpStraight
338 : },
339 : { std::string("flag8thDown"),
340 : std::string("flag8thDownStraight"),
341 : SymId::flag8thDownStraight
342 : },
343 : { std::string("flag8thUp"),
344 : std::string("flag8thUpStraight"),
345 : SymId::flag8thUpStraight
346 : },
347 : // Time sig variants
348 : { std::string("timeSig0"),
349 : std::string("timeSig0Large"),
350 : SymId::timeSig0Large
351 : },
352 : { std::string("timeSig0"),
353 : std::string("timeSig0Small"),
354 : SymId::timeSig0Small
355 : },
356 : { std::string("timeSig0"),
357 : std::string("timeSig0Narrow"),
358 : SymId::timeSig0Narrow
359 : },
360 : { std::string("timeSig1"),
361 : std::string("timeSig1Large"),
362 : SymId::timeSig1Large
363 : },
364 : { std::string("timeSig1"),
365 : std::string("timeSig1Small"),
366 : SymId::timeSig1Small
367 : },
368 : { std::string("timeSig1"),
369 : std::string("timeSig1Narrow"),
370 : SymId::timeSig1Narrow
371 : },
372 : { std::string("timeSig2"),
373 : std::string("timeSig2Large"),
374 : SymId::timeSig2Large
375 : },
376 : { std::string("timeSig2"),
377 : std::string("timeSig2Small"),
378 : SymId::timeSig2Small
379 : },
380 : { std::string("timeSig2"),
381 : std::string("timeSig2Narrow"),
382 : SymId::timeSig2Narrow
383 : },
384 : { std::string("timeSig3"),
385 : std::string("timeSig3Large"),
386 : SymId::timeSig3Large
387 : },
388 : { std::string("timeSig3"),
389 : std::string("timeSig3Small"),
390 : SymId::timeSig3Small
391 : },
392 : { std::string("timeSig3"),
393 : std::string("timeSig3Narrow"),
394 : SymId::timeSig3Narrow
395 : },
396 : { std::string("timeSig4"),
397 : std::string("timeSig4Large"),
398 : SymId::timeSig4Large
399 : },
400 : { std::string("timeSig4"),
401 : std::string("timeSig4Small"),
402 : SymId::timeSig4Small
403 : },
404 : { std::string("timeSig4"),
405 : std::string("timeSig4Narrow"),
406 : SymId::timeSig4Narrow
407 : },
408 : { std::string("timeSig5"),
409 : std::string("timeSig5Large"),
410 : SymId::timeSig5Large
411 : },
412 : { std::string("timeSig5"),
413 : std::string("timeSig5Small"),
414 : SymId::timeSig5Small
415 : },
416 : { std::string("timeSig5"),
417 : std::string("timeSig5Narrow"),
418 : SymId::timeSig5Narrow
419 : },
420 : { std::string("timeSig6"),
421 : std::string("timeSig6Large"),
422 : SymId::timeSig6Large
423 : },
424 : { std::string("timeSig6"),
425 : std::string("timeSig6Small"),
426 : SymId::timeSig6Small
427 : },
428 : { std::string("timeSig6"),
429 : std::string("timeSig6Narrow"),
430 : SymId::timeSig6Narrow
431 : },
432 : { std::string("timeSig7"),
433 : std::string("timeSig7Large"),
434 : SymId::timeSig7Large
435 : },
436 : { std::string("timeSig7"),
437 : std::string("timeSig7Small"),
438 : SymId::timeSig7Small
439 : },
440 : { std::string("timeSig7"),
441 : std::string("timeSig7Narrow"),
442 : SymId::timeSig7Narrow
443 : },
444 : { std::string("timeSig8"),
445 : std::string("timeSig8Large"),
446 : SymId::timeSig8Large
447 : },
448 : { std::string("timeSig8"),
449 : std::string("timeSig8Small"),
450 : SymId::timeSig8Small
451 : },
452 : { std::string("timeSig8"),
453 : std::string("timeSig8Narrow"),
454 : SymId::timeSig8Narrow
455 : },
456 : { std::string("timeSig9"),
457 : std::string("timeSig9Large"),
458 : SymId::timeSig9Large
459 : },
460 : { std::string("timeSig9"),
461 : std::string("timeSig9Small"),
462 : SymId::timeSig9Small
463 : },
464 : { std::string("timeSig9"),
465 : std::string("timeSig9Narrow"),
466 : SymId::timeSig9Narrow
467 : },
468 : { std::string("timeSigBracketLeftSmall"),
469 : std::string("timeSigBracketLeftSmallLarge"),
470 : SymId::timeSigBracketLeftSmallLarge
471 : },
472 : { std::string("timeSigBracketLeftSmall"),
473 : std::string("timeSigBracketLeftSmallNarrow"),
474 : SymId::timeSigBracketLeftSmallNarrow
475 : },
476 : { std::string("timeSigBracketRightSmall"),
477 : std::string("timeSigBracketRightSmallLarge"),
478 : SymId::timeSigBracketRightSmallLarge
479 : },
480 : { std::string("timeSigBracketRightSmall"),
481 : std::string("timeSigBracketRightSmallNarrow"),
482 : SymId::timeSigBracketRightSmallNarrow
483 : },
484 : { std::string("timeSigCommon"),
485 : std::string("timeSigCommonLarge"),
486 : SymId::timeSigCommonLarge
487 : },
488 : { std::string("timeSigCommon"),
489 : std::string("timeSigCommonNarrow"),
490 : SymId::timeSigCommonNarrow
491 : },
492 : { std::string("timeSigCutCommon"),
493 : std::string("timeSigCutCommonLarge"),
494 : SymId::timeSigCutCommonLarge
495 : },
496 : { std::string("timeSigCutCommon"),
497 : std::string("timeSigCutCommonNarrow"),
498 : SymId::timeSigCutCommonNarrow
499 : },
500 : { std::string("timeSigPlus"),
501 : std::string("timeSigPlusLarge"),
502 : SymId::timeSigPlusLarge
503 : },
504 : { std::string("timeSigPlus"),
505 : std::string("timeSigPlusNarrow"),
506 : SymId::timeSigPlusNarrow
507 : },
508 : { std::string("timeSigPlusSmall"),
509 : std::string("timeSigPlusSmallLarge"),
510 : SymId::timeSigPlusSmallLarge
511 : },
512 : { std::string("timeSigPlusSmall"),
513 : std::string("timeSigPlusSmallNarrow"),
514 : SymId::timeSigPlusSmallNarrow
515 : },
516 : { std::string("timeSigFractionalSlash"),
517 : std::string("timeSigFractionalSlashLarge"),
518 : SymId::timeSigFractionalSlashLarge
519 : },
520 : { std::string("timeSigFractionalSlash"),
521 : std::string("timeSigFractionalSlashNarrow"),
522 : SymId::timeSigFractionalSlashNarrow
523 : },
524 : { std::string("timeSigEquals"),
525 : std::string("timeSigEqualsLarge"),
526 : SymId::timeSigEqualsLarge
527 : },
528 : { std::string("timeSigEquals"),
529 : std::string("timeSigEqualsNarrow"),
530 : SymId::timeSigEqualsNarrow
531 : },
532 : { std::string("timeSigMinus"),
533 : std::string("timeSigMinusLarge"),
534 : SymId::timeSigMinusLarge
535 : },
536 : { std::string("timeSigMinus"),
537 : std::string("timeSigMinusNarrow"),
538 : SymId::timeSigMinusNarrow
539 : },
540 : { std::string("timeSigMultiply"),
541 : std::string("timeSigMultiplyLarge"),
542 : SymId::timeSigMultiplyLarge
543 : },
544 : { std::string("timeSigMultiply"),
545 : std::string("timeSigMultiplyNarrow"),
546 : SymId::timeSigMultiplyNarrow
547 : },
548 : { std::string("timeSigParensLeftSmall"),
549 : std::string("timeSigParensLeftSmallLarge"),
550 : SymId::timeSigParensLeftSmallLarge
551 : },
552 : { std::string("timeSigParensLeftSmall"),
553 : std::string("timeSigParensLeftSmallNarrow"),
554 : SymId::timeSigParensLeftSmallNarrow
555 : },
556 : { std::string("timeSigParensRightSmall"),
557 : std::string("timeSigParensRightSmallLarge"),
558 : SymId::timeSigParensRightSmallLarge
559 : },
560 : { std::string("timeSigParensRightSmall"),
561 : std::string("timeSigParensRightSmallNarrow"),
562 : SymId::timeSigParensRightSmallNarrow
563 : },
564 : { std::string("timeSigParensLeft"),
565 : std::string("timeSigParensLeftLarge"),
566 : SymId::timeSigParensLeftLarge
567 : },
568 : { std::string("timeSigParensLeft"),
569 : std::string("timeSigParensLeftNarrow"),
570 : SymId::timeSigParensLeftNarrow
571 : },
572 : { std::string("timeSigParensRight"),
573 : std::string("timeSigParensRightLarge"),
574 : SymId::timeSigParensRightLarge
575 : },
576 : { std::string("timeSigParensRight"),
577 : std::string("timeSigParensRightNarrow"),
578 : SymId::timeSigParensRightNarrow
579 : },
580 : { std::string("timeSigCommaEquals"),
581 : std::string("timeSigCommaEqualsLarge"),
582 : SymId::timeSigCommaEqualsLarge
583 : },
584 : { std::string("timeSigCommaEquals"),
585 : std::string("timeSigCommaEqualsNarrow"),
586 : SymId::timeSigCommaEqualsNarrow
587 : },
588 : { std::string("timeSigFractionQuarter"),
589 : std::string("timeSigFractionQuarterLarge"),
590 : SymId::timeSigFractionQuarterLarge
591 : },
592 : { std::string("timeSigFractionQuarter"),
593 : std::string("timeSigFractionQuarterNarrow"),
594 : SymId::timeSigFractionQuarterNarrow
595 : },
596 : { std::string("timeSigFractionHalf"),
597 : std::string("timeSigFractionHalfLarge"),
598 : SymId::timeSigFractionHalfLarge
599 : },
600 : { std::string("timeSigFractionHalf"),
601 : std::string("timeSigFractionHalfNarrow"),
602 : SymId::timeSigFractionHalfNarrow
603 : },
604 : { std::string("timeSigFractionThreeQuarters"),
605 : std::string("timeSigFractionThreeQuartersLarge"),
606 : SymId::timeSigFractionThreeQuartersLarge
607 : },
608 : { std::string("timeSigFractionThreeQuarters"),
609 : std::string("timeSigFractionThreeQuartersNarrow"),
610 : SymId::timeSigFractionThreeQuartersNarrow
611 : },
612 : { std::string("timeSigFractionOneThird"),
613 : std::string("timeSigFractionOneThirdLarge"),
614 : SymId::timeSigFractionOneThirdLarge
615 : },
616 : { std::string("timeSigFractionOneThird"),
617 : std::string("timeSigFractionOneThirdNarrow"),
618 : SymId::timeSigFractionOneThirdNarrow
619 : },
620 : { std::string("timeSigFractionTwoThirds"),
621 : std::string("timeSigFractionTwoThirdsLarge"),
622 : SymId::timeSigFractionTwoThirdsLarge
623 : },
624 : { std::string("timeSigFractionTwoThirds"),
625 : std::string("timeSigFractionTwoThirdsNarrow"),
626 : SymId::timeSigFractionTwoThirdsNarrow
627 : },
628 : { std::string("timeSigX"),
629 : std::string("timeSigXLarge"),
630 : SymId::timeSigXLarge
631 : },
632 : { std::string("timeSigX"),
633 : std::string("timeSigXNarrow"),
634 : SymId::timeSigXNarrow
635 : },
636 : { std::string("timeSigOpenPenderecki"),
637 : std::string("timeSigOpenPendereckiLarge"),
638 : SymId::timeSigOpenPendereckiLarge
639 : },
640 : { std::string("timeSigOpenPenderecki"),
641 : std::string("timeSigOpenPendereckiNarrow"),
642 : SymId::timeSigOpenPendereckiNarrow
643 : },
644 : { std::string("timeSigCut2"),
645 : std::string("timeSigCut2Large"),
646 : SymId::timeSigCut2Large
647 : },
648 : { std::string("timeSigCut2"),
649 : std::string("timeSigCut2Narrow"),
650 : SymId::timeSigCut2Narrow
651 : },
652 : { std::string("timeSigCut3"),
653 : std::string("timeSigCut3Large"),
654 : SymId::timeSigCut3Large
655 : },
656 : { std::string("timeSigCut3"),
657 : std::string("timeSigCut3Narrow"),
658 : SymId::timeSigCut3Narrow
659 : },
660 : };
661 :
662 128 : void EngravingFont::loadStylisticAlternates(const JsonObject& glyphsWithAlternatesObject)
663 : {
664 128 : if (!glyphsWithAlternatesObject.isValid()) {
665 48 : return;
666 : }
667 :
668 : bool ok;
669 8800 : for (const GlyphWithAlternates& glyph : GLYPHS_WITH_ALTERNATES) {
670 8720 : if (glyphsWithAlternatesObject.contains(glyph.key)) {
671 13152 : const JsonArray alternatesArray = glyphsWithAlternatesObject.value(glyph.key).toObject().value("alternates").toArray();
672 :
673 4384 : JsonValue val;
674 7152 : for (size_t i = 0; i < alternatesArray.size(); ++i) {
675 6912 : JsonValue v = alternatesArray.at(i);
676 20736 : if (v.toObject().value("name").toStdString() == glyph.alternateKey) {
677 4144 : val = v;
678 4144 : break;
679 : }
680 6912 : }
681 :
682 4384 : if (!val.isNull()) {
683 4144 : JsonObject symObj = val.toObject();
684 4144 : Sym& sym = this->sym(glyph.alternateSymId);
685 :
686 4144 : Smufl::Code code;
687 12432 : char32_t smuflCode = symObj.value("codepoint").toString().mid(2).toUInt(&ok, 16);
688 4144 : if (ok) {
689 4144 : code.smuflCode = smuflCode;
690 : }
691 :
692 12432 : char32_t musicSymBlockCode = symObj.value("alternateCodepoint").toString().mid(2).toUInt(&ok, 16);
693 4144 : if (ok) {
694 0 : code.musicSymBlockCode = musicSymBlockCode;
695 : }
696 :
697 4144 : if (code.smuflCode || code.musicSymBlockCode) {
698 4144 : computeMetrics(sym, code);
699 : }
700 4144 : }
701 4384 : }
702 : }
703 : }
704 :
705 128 : void EngravingFont::loadGlyphsWithAnchors(const JsonObject& glyphsWithAnchors)
706 : {
707 128 : if (!glyphsWithAnchors.isValid()) {
708 0 : return;
709 : }
710 :
711 42448 : for (const std::string& symName : glyphsWithAnchors.keys()) {
712 42320 : SymId symId = SymNames::symIdByName(symName);
713 :
714 42320 : if (symId == SymId::noSym) {
715 278880 : for (auto& alternate : GLYPHS_WITH_ALTERNATES) {
716 276352 : if (alternate.alternateKey == symName) {
717 96 : symId = alternate.alternateSymId;
718 96 : break;
719 : }
720 : }
721 : }
722 :
723 42320 : if (symId == SymId::noSym) {
724 : //! NOTE currently, Bravura contains a bunch of entries in glyphsWithAnchors
725 : //! for glyph names that will not be found - flag32ndUpStraight, etc.
726 2528 : continue;
727 : }
728 :
729 39792 : Sym& sym = this->sym(symId);
730 39792 : const JsonObject anchors = glyphsWithAnchors.value(symName).toObject();
731 39792 : if (!anchors.isValid()) {
732 0 : continue;
733 : }
734 :
735 : static const std::unordered_map<std::string, SmuflAnchorId> smuflAnchorIdNames {
736 0 : { "stemDownNW", SmuflAnchorId::stemDownNW },
737 0 : { "stemUpSE", SmuflAnchorId::stemUpSE },
738 0 : { "stemDownSW", SmuflAnchorId::stemDownSW },
739 0 : { "stemUpNW", SmuflAnchorId::stemUpNW },
740 0 : { "cutOutNE", SmuflAnchorId::cutOutNE },
741 0 : { "cutOutNW", SmuflAnchorId::cutOutNW },
742 0 : { "cutOutSE", SmuflAnchorId::cutOutSE },
743 0 : { "cutOutSW", SmuflAnchorId::cutOutSW },
744 0 : { "opticalCenter", SmuflAnchorId::opticalCenter },
745 39968 : };
746 :
747 112272 : for (const std::string& anchorId : anchors.keys()) {
748 72480 : const auto search = smuflAnchorIdNames.find(anchorId);
749 72480 : if (search == smuflAnchorIdNames.cend()) {
750 : //LOGD() << "Unhandled SMuFL anchorId: " << anchorId;
751 11440 : continue;
752 : }
753 :
754 61040 : const JsonArray arr = anchors.value(anchorId).toArray();
755 61040 : const double x = arr.at(0).toDouble();
756 61040 : const double y = arr.at(1).toDouble();
757 61040 : const double defaultSpatium = StyleDef::styleValues[static_cast<size_t>(Sid::spatium)].defaultValue.toDouble();
758 61040 : sym.smuflAnchors[search->second] = PointF(x, -y) * defaultSpatium;
759 100832 : }
760 39920 : }
761 16 : }
762 :
763 128 : void EngravingFont::loadEngravingDefaults(const JsonObject& engravingDefaultsObject)
764 : {
765 128 : if (!engravingDefaultsObject.isValid()) {
766 0 : return;
767 : }
768 :
769 : struct EngravingDefault {
770 : std::vector<Sid> sids;
771 :
772 : // If a childKey is not specified in `engravingDefaultsObject`,
773 : // it will receive the value for the key of `this` EngravingDefault.
774 : // This is done for compatibility with fonts made for older SMuFL versions:
775 : // in newer versions, some settings have been split into two.
776 : std::vector<std::string> childKeys = {};
777 :
778 384 : EngravingDefault(const std::vector<Sid>& sids)
779 384 : : sids(sids), childKeys() {}
780 16 : EngravingDefault(const std::vector<Sid>& sids, const std::vector<std::string>& childKeys)
781 16 : : sids(sids), childKeys(childKeys) {}
782 : };
783 :
784 : // https://w3c.github.io/smufl/latest/specification/engravingdefaults.html
785 : static const std::unordered_map<std::string, EngravingDefault> engravingDefaultsMapping = {
786 : // "textFontFamily" not supported
787 : { "staffLineThickness", { { Sid::staffLineWidth } } },
788 : { "stemThickness", { { Sid::stemWidth } } },
789 : { "beamThickness", { { Sid::beamWidth } } },
790 : // "beamSpacing" handled separately
791 : { "legerLineThickness", { { Sid::ledgerLineWidth } } },
792 : { "legerLineExtension", { { Sid::ledgerLineLength } } },
793 : { "slurEndpointThickness", { { Sid::slurEndWidth } } },
794 : { "slurMidpointThickness", { { Sid::slurMidWidth } } },
795 : { "tieEndpointThickness", { { Sid::tieEndWidth } } },
796 : { "tieMidpointThickness", { { Sid::tieMidWidth } } },
797 : { "thinBarlineThickness", { { Sid::barWidth, Sid::doubleBarWidth } } },
798 : { "thickBarlineThickness", { { Sid::endBarWidth } } },
799 : { "dashedBarlineThickness", { { Sid::dashBarWidth } } },
800 : { "dashedBarlineDashLength", { { Sid::dashBarDash } } },
801 : { "dashedBarlineGapLength", { { Sid::dashBarGap } } },
802 : { "barlineSeparation", { { Sid::doubleBarDistance }, { "thinThickBarlineSeparation" } } },
803 : { "thinThickBarlineSeparation", { { Sid::endBarDistance } } },
804 : { "repeatBarlineDotSeparation", { { Sid::repeatBarlineDotSeparation } } },
805 : { "bracketThickness", { { Sid::bracketWidth } } },
806 : // "subBracketThickness" not supported
807 : { "hairpinThickness", { { Sid::hairpinLineWidth } } },
808 : { "octaveLineThickness", { { Sid::ottavaLineWidth } } },
809 : { "pedalLineThickness", { { Sid::pedalLineWidth } } },
810 : { "repeatEndingLineThickness", { { Sid::voltaLineWidth } } },
811 : // "arrowShaftThickness" not supported
812 : { "lyricLineThickness", { { Sid::lyricsLineThickness } } },
813 : // "textEnclosureThickness" handled separately
814 : { "tupletBracketThickness", { { Sid::tupletBracketWidth } } },
815 : { "hBarThickness", { { Sid::mmRestHBarThickness } } }
816 976 : };
817 :
818 128 : std::function<void(const std::string& key, const PropertyValue& value)> applyEngravingDefault;
819 :
820 0 : applyEngravingDefault = [&](const std::string& key, const PropertyValue& value) {
821 3392 : auto search = engravingDefaultsMapping.find(key);
822 3392 : if (search != engravingDefaultsMapping.cend()) {
823 6336 : for (Sid sid : search->second.sids) {
824 3232 : m_engravingDefaults.insert({ sid, value });
825 : }
826 :
827 3232 : for (const std::string& childKey : search->second.childKeys) {
828 128 : if (!engravingDefaultsObject.contains(childKey)) {
829 64 : applyEngravingDefault(childKey, value);
830 : }
831 : }
832 : }
833 3520 : };
834 :
835 3712 : for (const std::string& key : engravingDefaultsObject.keys()) {
836 3584 : if (key == "textEnclosureThickness") {
837 128 : m_textEnclosureThickness = engravingDefaultsObject.value(key).toDouble();
838 128 : continue;
839 : }
840 :
841 3456 : if (key == "beamSpacing") {
842 128 : bool value = engravingDefaultsObject.value(key).toDouble() > 0.75;
843 128 : m_engravingDefaults.insert({ Sid::useWideBeams, value });
844 128 : continue;
845 128 : }
846 :
847 3328 : applyEngravingDefault(key, engravingDefaultsObject.value(key).toDouble());
848 128 : }
849 :
850 128 : m_engravingDefaults.insert({ Sid::musicalTextFont, String(u"%1 Text").arg(String::fromStdString(m_family)) });
851 544 : }
852 :
853 379440 : void EngravingFont::computeMetrics(EngravingFont::Sym& sym, const Smufl::Code& code)
854 : {
855 379440 : if (fontProvider()->inFont(m_font, code.smuflCode)) {
856 139136 : sym.code = code.smuflCode;
857 240304 : } else if (fontProvider()->inFont(m_font, code.musicSymBlockCode)) {
858 48 : sym.code = code.musicSymBlockCode;
859 : }
860 :
861 379440 : if (sym.code > 0) {
862 139184 : sym.bbox = fontProvider()->boundingRect(m_font, sym.code);
863 139184 : sym.advance = fontProvider()->horizontalAdvance(m_font, sym.code);
864 : }
865 379440 : }
866 :
867 : // =============================================
868 : // Symbol properties
869 : // =============================================
870 :
871 88352 : EngravingFont::Sym& EngravingFont::sym(SymId id)
872 : {
873 88352 : return m_symbols[static_cast<size_t>(id)];
874 : }
875 :
876 6649890 : const EngravingFont::Sym& EngravingFont::sym(SymId id) const
877 : {
878 6649890 : return m_symbols.at(static_cast<size_t>(id));
879 : }
880 :
881 2339802 : char32_t EngravingFont::symCode(SymId id) const
882 : {
883 2339802 : const Sym& s = sym(id);
884 2339802 : if (s.isValid()) {
885 2296254 : return s.code;
886 : }
887 :
888 : // fallback: search in the common SMuFL table
889 43548 : return Smufl::smuflCode(id);
890 : }
891 :
892 133 : SymId EngravingFont::fromCode(char32_t code) const
893 : {
894 131237 : auto it = std::find_if(m_symbols.begin(), m_symbols.end(), [code](const Sym& s) { return s.code == code; });
895 133 : return static_cast<SymId>(it == m_symbols.end() ? 0 : it - m_symbols.begin());
896 : }
897 :
898 2336312 : String EngravingFont::toString(SymId id) const
899 : {
900 2336312 : return String::fromUcs4(symCode(id));
901 : }
902 :
903 3264 : bool EngravingFont::isValid(SymId id) const
904 : {
905 3264 : return sym(id).isValid();
906 : }
907 :
908 2158722 : bool EngravingFont::useFallbackFont(SymId id) const
909 : {
910 2158722 : return MScore::useFallbackFont && !sym(id).isValid() && !engravingFonts()->isFallbackFont(this);
911 : }
912 :
913 : // =============================================
914 : // Symbol bounding box
915 : // =============================================
916 :
917 1365043 : RectF EngravingFont::bbox(SymId id, double mag) const
918 : {
919 1365043 : return bbox(id, SizeF(mag, mag));
920 : }
921 :
922 1396554 : RectF EngravingFont::bbox(SymId id, const SizeF& mag) const
923 : {
924 1396554 : if (useFallbackFont(id)) {
925 6599 : return engravingFonts()->fallbackFont()->bbox(id, mag);
926 : }
927 :
928 1389955 : RectF r = sym(id).bbox;
929 1389955 : return r.scale(mag);
930 : }
931 :
932 3774 : RectF EngravingFont::bbox(const SymIdList& s, double mag) const
933 : {
934 3774 : return bbox(s, SizeF(mag, mag));
935 : }
936 :
937 16348 : RectF EngravingFont::bbox(const SymIdList& s, const SizeF& mag) const
938 : {
939 16348 : RectF r;
940 16348 : PointF pos;
941 37922 : for (SymId id : s) {
942 21574 : r.unite(bbox(id, mag).translated(pos));
943 21574 : pos.rx() += advance(id, mag.width());
944 : }
945 32696 : return r;
946 : }
947 :
948 232 : Shape EngravingFont::shape(const SymIdList& s, double mag) const
949 : {
950 232 : return shape(s, SizeF(mag, mag));
951 : }
952 :
953 232 : Shape EngravingFont::shape(const SymIdList& s, const SizeF& mag) const
954 : {
955 232 : Shape sh;
956 232 : PointF pos(0.0, 0.0);
957 3438 : for (SymId id : s) {
958 3206 : sh.add(Shape(bbox(id, mag)).translate(pos));
959 3206 : pos.rx() += advance(id, mag.width());
960 : }
961 232 : return sh;
962 0 : }
963 :
964 43520 : Shape EngravingFont::shapeWithCutouts(SymId id, double mag)
965 : {
966 43520 : return shapeWithCutouts(id, SizeF(mag, mag));
967 : }
968 :
969 43520 : Shape EngravingFont::shapeWithCutouts(SymId id, const SizeF& mag)
970 : {
971 43520 : Shape& shape = sym(id).shapeWithCutouts;
972 43520 : if (shape.empty()) {
973 301 : constructShapeWithCutouts(shape, id);
974 : }
975 :
976 43520 : return shape.scaled(mag);
977 : }
978 :
979 301 : void EngravingFont::constructShapeWithCutouts(Shape& shape, SymId id)
980 : {
981 301 : RectF boundingBox = bbox(id, 1.0);
982 301 : double bottom = boundingBox.bottom();
983 301 : double top = boundingBox.top();
984 301 : double left = boundingBox.left();
985 301 : double right = boundingBox.right();
986 :
987 301 : PointF cutOutNW = smuflAnchor(id, SmuflAnchorId::cutOutNW, 1.0);
988 301 : PointF cutOutNE = smuflAnchor(id, SmuflAnchorId::cutOutNE, 1.0);
989 301 : PointF cutOutSW = smuflAnchor(id, SmuflAnchorId::cutOutSW, 1.0);
990 301 : PointF cutOutSE = smuflAnchor(id, SmuflAnchorId::cutOutSE, 1.0);
991 :
992 301 : bool nwNull = cutOutNW.isNull();
993 301 : bool neNull = cutOutNE.isNull();
994 301 : bool swNull = cutOutSW.isNull();
995 301 : bool seNull = cutOutSE.isNull();
996 :
997 301 : if (nwNull && neNull && swNull && seNull) {
998 105 : shape = Shape(bbox(id, 1.0));
999 105 : return;
1000 : }
1001 :
1002 196 : if (nwNull) {
1003 64 : cutOutNW = PointF(left, top);
1004 : }
1005 196 : if (neNull) {
1006 48 : cutOutNE = PointF(right, top);
1007 : }
1008 196 : if (swNull) {
1009 80 : cutOutSW = PointF(left, bottom);
1010 : }
1011 196 : if (seNull) {
1012 11 : cutOutSE = PointF(right, bottom);
1013 : }
1014 :
1015 196 : double leftInset = std::max(cutOutNW.x(), cutOutSW.x());
1016 196 : double rightInset = std::min(cutOutNE.x(), cutOutSE.x());
1017 196 : double topInset = std::max(cutOutNW.y(), cutOutNE.y());
1018 196 : double bottomInset = std::min(cutOutSW.y(), cutOutSE.y());
1019 :
1020 196 : std::vector<RectF> rects;
1021 196 : rects.reserve(6); //at most
1022 :
1023 : // bottom rect
1024 196 : rects.emplace_back(RectF(PointF(cutOutSW.x(), bottom), PointF(cutOutSE.x(), topInset)).normalized());
1025 : // right rect
1026 196 : bool rightRectPlaced = false;
1027 196 : if (!seNull) {
1028 185 : rects.emplace_back(RectF(PointF(right, cutOutSE.y()), PointF(leftInset, cutOutNE.y())).normalized());
1029 185 : rightRectPlaced = true;
1030 : }
1031 : // top rect
1032 196 : bool topRectPlaced = false;
1033 196 : if (!rightRectPlaced || !neNull) {
1034 148 : rects.emplace_back(RectF(PointF(cutOutNW.x(), top), PointF(cutOutNE.x(), bottomInset)).normalized());
1035 148 : topRectPlaced = true;
1036 : }
1037 : // left rect
1038 196 : if (!topRectPlaced || !nwNull) {
1039 155 : rects.emplace_back(RectF(PointF(left, cutOutSW.y()), PointF(rightInset, cutOutNW.y())).normalized());
1040 : }
1041 : // center horizontal rect if needed
1042 196 : if (leftInset > rightInset && topInset < bottomInset) {
1043 12 : rects.emplace_back(RectF(PointF(left, bottomInset), PointF(right, topInset)).normalized());
1044 : }
1045 : // center vertical rect if needed
1046 196 : if (leftInset < rightInset && topInset > bottomInset) {
1047 2 : rects.emplace_back(RectF(PointF(leftInset, bottom), PointF(rightInset, top)).normalized());
1048 : }
1049 :
1050 196 : shape = Shape(rects);
1051 196 : }
1052 :
1053 : // =============================================
1054 : // Symbol metrics
1055 : // =============================================
1056 :
1057 615039 : double EngravingFont::width(SymId id, double mag) const
1058 : {
1059 615039 : return bbox(id, mag).width();
1060 : }
1061 :
1062 8218 : double EngravingFont::height(SymId id, double mag) const
1063 : {
1064 8218 : return bbox(id, mag).height();
1065 : }
1066 :
1067 27560 : double EngravingFont::advance(SymId id, double mag) const
1068 : {
1069 27560 : if (useFallbackFont(id)) {
1070 1562 : return engravingFonts()->fallbackFont()->advance(id, mag);
1071 : }
1072 :
1073 25998 : return sym(id).advance * mag;
1074 : }
1075 :
1076 0 : double EngravingFont::width(const SymIdList& s, double mag) const
1077 : {
1078 0 : return bbox(s, mag).width();
1079 : }
1080 :
1081 734608 : PointF EngravingFont::smuflAnchor(SymId symId, SmuflAnchorId anchorId, double mag) const
1082 : {
1083 734608 : if (useFallbackFont(symId)) {
1084 2710 : return engravingFonts()->fallbackFont()->smuflAnchor(symId, anchorId, mag);
1085 : }
1086 :
1087 731898 : const std::map<SmuflAnchorId, PointF>& smuflAnchors = sym(symId).smuflAnchors;
1088 :
1089 731898 : auto it = smuflAnchors.find(anchorId);
1090 731898 : if (it == smuflAnchors.cend()) {
1091 24910 : return PointF();
1092 : }
1093 :
1094 706988 : return it->second * mag;
1095 : }
1096 :
1097 : // =============================================
1098 : // Draw
1099 : // =============================================
1100 :
1101 251 : void EngravingFont::draw(SymId id, Painter* painter, const SizeF& mag, const PointF& pos, const double angle) const
1102 : {
1103 251 : const Sym& sym = this->sym(id);
1104 251 : if (sym.isCompound()) { // is this a compound symbol?
1105 0 : draw(sym.subSymbolIds, painter, mag, pos, angle);
1106 0 : return;
1107 : }
1108 :
1109 251 : if (!sym.isValid()) {
1110 0 : if (MScore::useFallbackFont && !engravingFonts()->isFallbackFont(this)) {
1111 0 : engravingFonts()->fallbackFont()->draw(id, painter, mag, pos, angle);
1112 : } else {
1113 0 : LOGE() << "invalid sym: " << static_cast<size_t>(id);
1114 : }
1115 :
1116 0 : return;
1117 : }
1118 :
1119 251 : painter->save();
1120 251 : m_font.setPointSizeF(StyleDef::DEFAULT_SMUFL_POINT_SIZE());
1121 251 : painter->scale(mag.width(), mag.height());
1122 251 : painter->setFont(m_font);
1123 251 : if (angle != 0) {
1124 0 : const double _width = sym.bbox.width() / 2;
1125 0 : const double _height = sym.bbox.height() / 2;
1126 0 : painter->translate(_width, -_height);
1127 0 : painter->rotate(angle);
1128 0 : painter->translate(-_width, _height);
1129 : }
1130 251 : painter->drawSymbol(PointF(pos.x() / mag.width(), pos.y() / mag.height()), symCode(id));
1131 251 : painter->restore();
1132 : }
1133 :
1134 231 : void EngravingFont::draw(SymId id, Painter* painter, double mag, const PointF& pos, const double angle) const
1135 : {
1136 231 : draw(id, painter, SizeF(mag, mag), pos, angle);
1137 231 : }
1138 :
1139 0 : void EngravingFont::draw(const SymIdList& ids, Painter* painter, double mag, const PointF& startPos, const double angle) const
1140 : {
1141 0 : PointF pos(startPos);
1142 0 : for (SymId id : ids) {
1143 0 : draw(id, painter, mag, pos, angle);
1144 0 : pos.setX(pos.x() + advance(id, mag));
1145 : }
1146 0 : }
1147 :
1148 20 : void EngravingFont::draw(const SymIdList& ids, Painter* painter, const SizeF& mag, const PointF& startPos, const double angle) const
1149 : {
1150 20 : PointF pos(startPos);
1151 40 : for (SymId id : ids) {
1152 20 : draw(id, painter, mag, pos, angle);
1153 20 : pos.setX(pos.x() + advance(id, mag.width()));
1154 : }
1155 20 : }
|