Skip to content

Commit

Permalink
Merge pull request #24041 from miiizen/more-xml-fonts
Browse files Browse the repository at this point in the history
Support more symbol fonts used in xml files
  • Loading branch information
RomanPudashkin authored Aug 19, 2024
2 parents 682cfc8 + 18862d3 commit 64f7248
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 26 deletions.
40 changes: 40 additions & 0 deletions src/framework/global/types/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,27 @@ StringList String::search(const std::regex& re, std::initializer_list<int> match
return out;
}

StringList String::search(const std::wregex& re, std::initializer_list<int> matches, SplitBehavior behavior) const
{
std::wstring ws = toStdWString();

std::wsregex_token_iterator iter(ws.begin(), ws.end(), re, matches);
std::wsregex_token_iterator end;
std::vector<std::wstring> vec = { iter, end };

StringList out;
for (const std::wstring& s : vec) {
if (behavior == SplitBehavior::SkipEmptyParts && s.empty()) {
// skip
continue;
}
String sub = String::fromStdWString(s);
out.push_back(std::move(sub));
}

return out;
}

String& String::replace(const String& before, const String& after)
{
if (before == after) {
Expand Down Expand Up @@ -1036,6 +1057,25 @@ String& String::replace(const std::regex& re, const String& after)
return *this;
}

String& String::replace(const std::wregex& re, const String& after)
{
std::wstring afterw;
afterw = after.toStdWString();

std::wstring originw;
originw = toStdWString();
std::wstring replacedw = std::regex_replace(originw, re, afterw);

mutStr().clear();
mutStr().resize(replacedw.size());

for (size_t i = 0; i < replacedw.size(); ++i) {
mutStr()[i] = static_cast<char16_t>(replacedw.at(i));
}

return *this;
}

String& String::insert(size_t position, const String& str)
{
mutStr().insert(position, str.constStr());
Expand Down
2 changes: 2 additions & 0 deletions src/framework/global/types/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,11 @@ class String
StringList split(const String& str, SplitBehavior behavior = KeepEmptyParts) const;
StringList split(const std::regex& re, SplitBehavior behavior = KeepEmptyParts) const;
StringList search(const std::regex& re, std::initializer_list<int> matches, SplitBehavior behavior = KeepEmptyParts) const;
StringList search(const std::wregex& re, std::initializer_list<int> matches, SplitBehavior behavior = KeepEmptyParts) const;
String& replace(const String& before, const String& after);
String& replace(char16_t before, char16_t after);
String& replace(const std::regex& re, const String& after);
String& replace(const std::wregex& re, const String& after);
String& insert(size_t position, const String& str);
String& remove(const String& str) { return replace(str, String()); }
String& remove(const std::regex& rx) { return replace(rx, String()); }
Expand Down
82 changes: 57 additions & 25 deletions src/importexport/musicxml/internal/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,16 @@ static void addElemOffset(EngravingItem* el, track_idx_t track, const String& pl
bool found = false;
for (EngravingItem* existingEl : muse::values(_pass2.systemElements(), elTick.ticks())) {
if (el->type() == existingEl->type()) {
if (el->isTextBase()) {
TextBase* elText = toTextBase(el);
TextBase* existingText = toTextBase(existingEl);
if (elText->xmlText() == existingText->xmlText()) {
found = true;
break;
} else {
continue;
}
}
found = true;
break;
}
Expand Down Expand Up @@ -2030,11 +2040,10 @@ static void createTimeTick(const Score* score, const Fraction& tick, const staff
static String replacePartNameAccidentals(const String& partName)
{
String name = partName;
static const std::regex re("((^|\\s|\\u00A0)[ABCDEF][b#]($|\\s|\u00A0))");
static const std::wregex re(L"((^|\\s|\\u00A0)[ABCDEF][b#\\u00AF]($|\\s|\\u00A0))");
StringList res = name.search(re, { 1 }, SplitBehavior::SkipEmptyParts);

if (!res.empty()) {
String transp = res.at(0).replace(u"b", u"").replace(u"#", u"");
String transp = res.at(0).replace(u"b", u"").replace(u"¯", u"").replace(u"#", u"");
name.replace(re, transp);
return name;
}
Expand Down Expand Up @@ -3224,7 +3233,6 @@ void MusicXMLParserDirection::direction(const String& partId,
}
}

handleTempo();
handleRepeats(measure, tick + m_offset, measureHasCoda, segnos, delayedDirections);
handleNmiCmi(measure, tick + m_offset, delayedDirections);
handleFraction();
Expand Down Expand Up @@ -3743,13 +3751,16 @@ void MusicXMLParserDirection::directionType(std::vector<MusicXmlSpannerDesc>& st
if (m_e.name() == "metronome") {
m_metroText = metronome(m_tpoMetro);
} else if (m_e.name() == "words") {
m_enclosure = m_e.attribute("enclosure");
m_enclosure = m_e.attribute("enclosure");
m_fontFamily = m_e.attribute("font-family");
String nextPart = xmlpass2::nextPartOfFormattedString(m_e);

textToDynamic(nextPart);
textToCrescLine(nextPart);
handleTempo(nextPart);
m_wordsText += nextPart;
} else if (m_e.name() == "rehearsal") {
m_enclosure = m_e.attribute("enclosure");
m_enclosure = m_e.attribute("enclosure");
if (m_enclosure.empty()) {
m_enclosure = u"square"; // note different default
}
Expand Down Expand Up @@ -4464,18 +4475,50 @@ void MusicXMLParserDirection::handleChordSym(const Fraction& tick, HarmonyMap& h
m_wordsText.clear();
}

void MusicXMLParserDirection::handleTempo()
void MusicXMLParserDirection::handleTempo(String& wordsString)
{
if (!configuration()->inferTextType()) {
return;
}
// Pick up any tempo markings which may have been exported from Sibelius as <words>

// Map Sibelius' representation of note types to their SMuFL counterparts and duration types
static const MetronomeTextMap sibeliusSyms = {
{ u"y", { u"<sym>metNote32ndUp</sym>", DurationType::V_32ND } },
{ u"x", { u"<sym>metNote16thUp</sym>", DurationType::V_16TH } },
{ u"e", { u"<sym>metNote8thUp</sym>", DurationType::V_EIGHTH } },
{ u"q", { u"<sym>metNoteQuarterUp</sym>", DurationType::V_QUARTER } },
{ u"h", { u"<sym>metNoteHalfUp</sym>", DurationType::V_HALF } },
{ u"w", { u"<sym>metNoteWhole</sym>", DurationType::V_WHOLE } },
{ u"V", { u"<sym>metNoteDoubleWholeSquare</sym>", DurationType::V_BREVE } },
{ u"W", { u"<sym>metNoteDoubleWhole</sym>", DurationType::V_BREVE } }
};

static const MetronomeTextMap metTimesSyms = {
{ u"Œ", { u"<sym>metNoteQuarterUp</sym>", DurationType::V_QUARTER } },
{ u"Ó", { u"<sym>metNoteHalfUp</sym>", DurationType::V_HALF } },
};

MetronomeTextMap textMap;
if (m_pass1.exporterString().contains(u"sibelius")) {
textMap = sibeliusSyms;
} else if (m_fontFamily == u"MetTimes Plain") {
textMap = metTimesSyms;
}
if (textMap.empty()) {
return;
}
String substitutions;
for (auto& entry : textMap) {
substitutions.append(entry.first);
}

// Pick up any tempo markings which may have been exported as <words>
// eg. andante (q = c. 90)
// Sibelius uses a symbol font with the characters 'yxeqhVwW' each drawn as a different duration
// which we need to map to SMuFL syms
String plainWords = MScoreTextToMXML::toPlainText(m_wordsText.simplified());
String plainWords = MScoreTextToMXML::toPlainText(wordsString.simplified());

static const std::regex tempo(".*([yxeqhVwW])(\\.?)(?:\\s|\\u00A0)*=[^0-9]*([0-9]+).*");
static const std::wregex tempo(L".*([" + substitutions.toStdWString() + L"])(\\.?)(?:\\s|\\u00A0)*=[^0-9]*([0-9]+).*");
StringList tempoMatches = plainWords.search(tempo, { 1, 2, 3 }, SplitBehavior::SkipEmptyParts);

// Not a tempo
Expand All @@ -4488,27 +4531,16 @@ void MusicXMLParserDirection::handleTempo()
const String val = tempoMatches.at(dot ? 2 : 1);

const String dotStr = dot ? u"<sym>space</sym><sym>metAugmentationDot</sym>" : u"";
// Map Sibelius' representation of note types to their SMuFL counterparts and duration types
static const std::map<String, std::pair<String, DurationType> > syms = {
{ u"y", { u"<sym>metNote32ndUp</sym>", DurationType::V_32ND } },
{ u"x", { u"<sym>metNote16thUp</sym>", DurationType::V_16TH } },
{ u"e", { u"<sym>metNote8thUp</sym>", DurationType::V_EIGHTH } },
{ u"q", { u"<sym>metNoteQuarterUp</sym>", DurationType::V_QUARTER } },
{ u"h", { u"<sym>metNoteHalfUp</sym>", DurationType::V_HALF } },
{ u"w", { u"<sym>metNoteWhole</sym>", DurationType::V_WHOLE } },
{ u"V", { u"<sym>metNoteDoubleWholeSquare</sym>", DurationType::V_BREVE } },
{ u"W", { u"<sym>metNoteDoubleWhole</sym>", DurationType::V_BREVE } }
};

static const std::regex replace("(.*)[yxeqhVwW]\\.?((?:\\s|\\u00A0)*=[^0-9]*[0-9]+.*)");
const String newStr = u"$1" + syms.at(dur).first + dotStr + u"$2";
static const std::wregex replace(L"(.*)[" + substitutions.toStdWString() + L"]\\.?((?:\\s|\\u00A0)*=[^0-9]*[0-9]+.*)");
const String newStr = u"$1" + textMap.at(dur).first + dotStr + u"$2";
plainWords.replace(replace, newStr);
m_wordsText = plainWords;
wordsString = plainWords;

if (!val.empty() && !dur.empty()) {
bool ok;
double d = val.toDouble(&ok);
TDuration duration = TDuration(syms.at(dur).second);
TDuration duration = TDuration(textMap.at(dur).second);
duration.setDots(dot);

if (ok && duration.isValid()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ class MusicXMLParserDirection
Jump* findJump(const String& repeat) const;
void handleNmiCmi(Measure* measure, const Fraction& tick, DelayedDirectionsList& delayedDirections);
void handleChordSym(const Fraction& tick, HarmonyMap& harmonyMap);
void handleTempo();
void handleTempo(String& wordsString);
String matchRepeat(const String& plainWords) const;
void skipLogCurrElem();
bool isLikelyCredit(const Fraction& tick) const;
Expand Down Expand Up @@ -511,6 +511,7 @@ class MusicXMLParserDirection
Hairpin* m_inferredHairpinStart = nullptr;
GradualTempoChange* m_inferredTempoLineStart = nullptr;
StringList m_dynamicsList;
String m_fontFamily;
String m_enclosure;
String m_wordsText;
String m_metroText;
Expand Down
2 changes: 2 additions & 0 deletions src/importexport/musicxml/internal/musicxml/musicxmlsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ typedef std::vector<InferredPercInstr> InferredPercList;

typedef std::map<String, MusicXMLInstrument> MusicXMLInstruments;

typedef std::map<String, std::pair<String, DurationType> > MetronomeTextMap;

//---------------------------------------------------------
// MxmlSupport -- MusicXML import support functions
//---------------------------------------------------------
Expand Down

0 comments on commit 64f7248

Please sign in to comment.