LCOV - code coverage report
Current view: top level - src/importexport/musicxml/internal/shared - musicxmlfonthandler.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 92.9 % 98 91
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              : 
      23              : /**
      24              :  MusicXML font handling support.
      25              :  */
      26              : 
      27              : #include "musicxmlfonthandler.h"
      28              : 
      29              : #include "engraving/rw/xmlwriter.h"
      30              : 
      31              : using namespace mu::engraving;
      32              : 
      33              : namespace mu::iex::musicxml {
      34              : //---------------------------------------------------------
      35              : //   MScoreTextToMXML
      36              : //---------------------------------------------------------
      37              : 
      38          400 : MScoreTextToMusicXml::MScoreTextToMusicXml(const String& tag, const String& attr, const CharFormat& defFmt, const String& mtf)
      39          400 :     : attribs(attr), tagname(tag), oldFormat(defFmt), musicalTextFont(mtf)
      40              : {
      41              :     // set MusicXML defaults
      42          400 :     oldFormat.setBold(false);
      43          400 :     oldFormat.setItalic(false);
      44          400 :     oldFormat.setUnderline(false);
      45          400 : }
      46              : 
      47              : //---------------------------------------------------------
      48              : //   toPlainText
      49              : //    convert to plain text
      50              : //    naive implementation: simply remove all chars from '<' to '>'
      51              : //    typically used to remove formatting info from fields read
      52              : //    from MuseScore 1.3 file where they are stored as html, such as
      53              : //    part name and shortName
      54              : //---------------------------------------------------------
      55              : 
      56         3692 : String MScoreTextToMusicXml::toPlainText(const String& text)
      57              : {
      58         3692 :     String res;
      59         3692 :     bool inElem = false;
      60        41787 :     for (size_t i = 0; i < text.size(); ++i) {
      61        38095 :         Char ch = text.at(i);
      62        38095 :         if (ch == u'<') {
      63         1437 :             inElem = true;
      64        36658 :         } else if (ch == u'>') {
      65         1437 :             inElem = false;
      66              :         } else {
      67        35221 :             if (!inElem) {
      68        26772 :                 res += ch;
      69              :             }
      70              :         }
      71              :     }
      72              :     //LOGD("MScoreTextToMXML::toPlainText('%s') res '%s'", muPrintable(text), muPrintable(res));
      73         3692 :     return res;
      74            0 : }
      75              : 
      76              : //---------------------------------------------------------
      77              : //   toPlainTextPlusSymbols
      78              : //    convert to plain text plus <sym>[name]</sym> encoded symbols
      79              : //---------------------------------------------------------
      80              : 
      81          302 : String MScoreTextToMusicXml::toPlainTextPlusSymbols(const std::list<TextFragment>& list)
      82              : {
      83          302 :     String res;
      84          722 :     for (const TextFragment& f : list) {
      85          420 :         res += f.text;
      86              :     }
      87          302 :     return res;
      88            0 : }
      89              : 
      90              : //---------------------------------------------------------
      91              : //   plainTextPlusSymbolsSize
      92              : //---------------------------------------------------------
      93              : 
      94          326 : static int plainTextPlusSymbolsFragmentSize(const TextFragment& f)
      95              : {
      96          326 :     return f.columns();
      97              : }
      98              : 
      99              : //---------------------------------------------------------
     100              : //   plainTextPlusSymbolsSize
     101              : //---------------------------------------------------------
     102              : 
     103          214 : static int plainTextPlusSymbolsListSize(const std::list<TextFragment>& list)
     104              : {
     105          214 :     int res = 0;
     106          328 :     for (const TextFragment& f : list) {
     107          114 :         res += plainTextPlusSymbolsFragmentSize(f);
     108              :     }
     109          214 :     return res;
     110              : }
     111              : 
     112              : //---------------------------------------------------------
     113              : //   split
     114              : //---------------------------------------------------------
     115              : 
     116              : /**
     117              :  Split \a in into \a left, \a mid and \a right. Mid starts at \a pos and is \a len characters long.
     118              :  Pos and len refer to the representation returned by toPlainTextPlusSymbols().
     119              :  TODO Make sure surrogate pairs are handled correctly
     120              :  Return true if OK, false on error.
     121              :  */
     122              : 
     123          103 : bool MScoreTextToMusicXml::split(const std::list<TextFragment>& in, const int pos, const int len,
     124              :                                  std::list<TextFragment>& left, std::list<TextFragment>& mid, std::list<TextFragment>& right)
     125              : {
     126              :     //LOGD("MScoreTextToMXML::split in size %d pos %d len %d", plainTextPlusSymbolsListSize(in), pos, len);
     127              :     //LOGD("-> in");
     128              :     //dumpText(in);
     129              : 
     130          103 :     if (pos < 0 || len < 0) {
     131            0 :         return false;
     132              :     }
     133              : 
     134              :     // ensure output is empty at start
     135          103 :     left.clear();
     136          103 :     mid.clear();
     137          103 :     right.clear();
     138              : 
     139              :     // set pos to begin of first fragment
     140          103 :     std::list<TextFragment>::const_iterator fragmentNr = in.begin();
     141          103 :     TextFragment fragment;
     142          103 :     if (fragmentNr != in.end()) {
     143          103 :         fragment = *fragmentNr;
     144              :     }
     145          103 :     std::list<TextFragment>* currentDest = &left;
     146          103 :     int currentMaxSize = pos;
     147              : 
     148              :     // while text left
     149          315 :     while (fragmentNr != in.end()) {
     150          212 :         int destSize = plainTextPlusSymbolsListSize(*currentDest);
     151          212 :         int fragSize = plainTextPlusSymbolsFragmentSize(fragment);
     152              :         // if no room left in current destination (check applies only to left and mid)
     153          212 :         if ((currentDest != &right && destSize >= currentMaxSize)
     154          109 :             || currentDest == &right) {
     155              :             // move to next destination
     156          103 :             if (currentDest == &left) {
     157          103 :                 currentDest = &mid;
     158          103 :                 currentMaxSize = len;
     159            0 :             } else if (currentDest == &mid) {
     160            0 :                 currentDest = &right;
     161              :             }
     162              :         }
     163              :         // if current fragment fits in current destination (check applies only to left and mid)
     164          212 :         if ((currentDest != &right && destSize + fragSize <= currentMaxSize)
     165            2 :             || currentDest == &right) {
     166              :             // add it
     167          210 :             currentDest->push_back(fragment);
     168              :             // move to next fragment
     169          210 :             fragmentNr++;
     170          317 :             if (fragmentNr != in.end()) {
     171          107 :                 fragment = *fragmentNr;
     172              :             }
     173              :         } else {
     174              :             // split current fragment
     175            2 :             TextFragment rightPart = fragment.split(currentMaxSize - plainTextPlusSymbolsListSize(*currentDest));
     176              :             // add first part to current destination
     177            2 :             currentDest->push_back(fragment);
     178            2 :             fragment = rightPart;
     179            2 :         }
     180              :     }
     181              : 
     182              :     /*
     183              :     LOGD("-> left");
     184              :     dumpText(left);
     185              :     LOGD("-> mid");
     186              :     dumpText(mid);
     187              :     LOGD("-> right");
     188              :     dumpText(right);
     189              :      */
     190              : 
     191          103 :     return true;
     192          103 : }
     193              : 
     194              : //---------------------------------------------------------
     195              : //   writeTextFragments
     196              : //---------------------------------------------------------
     197              : 
     198          400 : void MScoreTextToMusicXml::writeTextFragments(const std::list<TextFragment>& fr, XmlWriter& xml)
     199              : {
     200              :     //dumpText(fr);
     201          400 :     bool firstTime = true;   // write additional attributes only the first time characters are written
     202          814 :     for (const TextFragment& f : fr) {
     203          414 :         newFormat = f.format;
     204          414 :         String formatAttr = updateFormat();
     205          414 :         xml.tagRaw(tagname + (firstTime ? attribs : String()) + formatAttr, f.text);
     206          414 :         firstTime = false;
     207          414 :     }
     208          400 : }
     209              : 
     210              : //---------------------------------------------------------
     211              : //   attribute
     212              : //    add one attribute if necessary
     213              : //---------------------------------------------------------
     214              : 
     215         2484 : static String attribute(bool needed, bool value, String trueString, String falseString)
     216              : {
     217         2484 :     String res;
     218         2484 :     if (needed) {
     219          123 :         res = value ? trueString : falseString;
     220              :     }
     221         2484 :     if (!res.empty()) {
     222          123 :         res = u" " + res;
     223              :     }
     224         2484 :     return res;
     225            0 : }
     226              : 
     227              : //---------------------------------------------------------
     228              : //   updateFormat
     229              : //    update the text format by generating attributes
     230              : //    corresponding to the difference between old- and newFormat
     231              : //    copy newFormat to oldFormat
     232              : //---------------------------------------------------------
     233              : 
     234          414 : String MScoreTextToMusicXml::updateFormat()
     235              : {
     236          414 :     if (newFormat.fontFamily() == "ScoreText") {
     237           10 :         newFormat.setFontFamily(musicalTextFont);
     238              :     }
     239          414 :     String res;
     240          414 :     res += attribute(newFormat.bold() != oldFormat.bold(), newFormat.bold(), u"font-weight=\"bold\"", u"font-weight=\"normal\"");
     241          414 :     res += attribute(newFormat.italic() != oldFormat.italic(), newFormat.italic(), u"font-style=\"italic\"", u"font-style=\"normal\"");
     242          414 :     res += attribute(newFormat.underline() != oldFormat.underline(), newFormat.underline(), u"underline=\"1\"", u"underline=\"0\"");
     243          414 :     res += attribute(newFormat.strike() != oldFormat.strike(), newFormat.strike(), u"line-through=\"1\"", u"line-though=\"0\"");
     244         1242 :     res += attribute(newFormat.fontFamily() != oldFormat.fontFamily(), true,
     245         1242 :                      String(u"font-family=\"%1\"").arg(newFormat.fontFamily()), u"");
     246          414 :     bool needSize = newFormat.fontSize() < 0.99 * oldFormat.fontSize() || newFormat.fontSize() > 1.01 * oldFormat.fontSize();
     247          414 :     res += attribute(needSize, true, String(u"font-size=\"%1\"").arg(newFormat.fontSize()), u"");
     248              :     //LOGD("updateFormat() res '%s'", muPrintable(res));
     249          414 :     oldFormat = newFormat;
     250          414 :     return res;
     251            0 : }
     252              : } // namespace Ms
        

Generated by: LCOV version 2.0-1