LCOV - code coverage report
Current view: top level - src/engraving/internal - engravingfont.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 83.8 % 333 279
Test Date: 2026-11-05 04:07:02

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

Generated by: LCOV version 2.0-1