[MusicXML export] invalid XML export: direction-type is missing child element

• Aug 17, 2019 - 10:13
Reported version
3.2
Type
Functional
Frequency
Few
Severity
S3 - Major
Reproducibility
Always
Status
closed
Regression
No
Workaround
No
Project

Comments

See https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/direc…

Content
Exactly one of the following

...

But here they are empty:

...
      <direction placement="above">
        <direction-type>
          </direction-type>
        <sound tempo="72"/>
        </direction>
      <direction placement="above">
        <direction-type>
          </direction-type>
        <sound tempo="72"/>
        </direction>
 ...

Here apparently lacking <words>...</words>?, but in other places it is just

      <direction>
        <direction-type>
          </direction-type>
        </direction>

And the fix for that it to have the keysig (3 sharps) at the first measure, not at the first after all mm-rests, then also the exported musicxml is clean!

I suspect the exporter assumes the TextBase passed into wordsMetronome() always has a valid layout, but this is not true for the TempoText in the failing cases (TextBase.isLayoutInvalid() returns true).

There's two places where setLayoutInvalid(); is called, one in the context of multi-measure rests, in mmrestrange.cpp:

bool MMRestRange::setProperty(Pid id, const PropertyValue& val)
{
    switch (id) {
    case Pid::MMREST_RANGE_BRACKET_TYPE:
        setBracketType(MMRestRangeBracketType(val.toInt()));
        setLayoutInvalid();
        triggerLayout();
        return true;
    default:
        return MeasureNumberBase::setProperty(id, val);
    }
}

The other in measurenumberbase.cpp:

bool MeasureNumberBase::setProperty(Pid id, const PropertyValue& val)
{
    switch (id) {
    case Pid::HPLACEMENT:
        setHPlacement(val.value<PlacementH>());
        setLayoutInvalid();
        triggerLayout();
        return true;
    default:
        return TextBase::setProperty(id, val);
    }
}

And except for the initialisation the only other place where it is set to true is in textbase.cpp:

void TextBase::setXmlText(const String& s)
{
    _text = s;
    textInvalid = false;
    layoutInvalid = true;
}

Seems that it goes wrong in TextBase::fragementList() which returns an empty list !?!

We never get into the inner for loop here:

std::list<TextFragment> TextBase::fragmentList() const
{
    std::list<TextFragment> res;
    for (const TextBlock& block : _layout) {
        for (const TextFragment& f : block.fragments()) {
            /* TODO TBD
            if (f.text.empty())                     // skip empty fragments, not to
                  continue;                           // insert extra HTML formatting
             */
            res.push_back(f);
            if (block.eol()) {
                // simply append a newline
                res.back().text += u"\n";
            }
        }
    }
    return res;
}

And the apparent trigger for all this mess seems to lay in the missing key sig at the start of the sample score (no keygsig, not just C-Major/a-minor, just none at all). But why???

It may not be related to multi measure rests at all?

But even with an initial keysig the export still looses the <font size="10"/><font face="Edwin"/><b>Andantino quasi Andante</b> tempo text
And import does strange things to the rehearsal marks A, B and C (but not D): it places them below the top staff rather than above. Probably an entirely differtent issue though

Attaching two simple test files, created by MuseScore 3.6.2, containing three empty measures and a tempo text. The multimeasure version still results in the MusicXML exporter finding an invalid layout in the TempoText (which probably explains the empty fragment list). This is with MuseScore 4, commit b443bf7 of June 1, running on Linux.

Same thing, trigger is the apparently missing initial key signature.
Take Tempo_measure_rest_362.mscz, disable mm rests, add a C-Major keysig to the first measure, enable mm rests, export to MusicXML -> no problem

Hmm, strange, it isn't quite that, the saved closed and reopened score has the same issue, but when doing this in one session it works, guess it does cause a re-layout?

And the invalid tempo text isn't the only issue, there's at least one other issue with empty direction-types, with (multi) measure numbers, not with your small sample score though, but with that Agnus Dei

For the tempo text I have a (crude) fix:

diff --git a/src/importexport/musicxml/internal/musicxml/exportxml.cpp b/src/importexport/musicxml/internal/musicxml/exportxml.cpp
index 075c7f3aae..cb928457d1 100644
--- a/src/importexport/musicxml/internal/musicxml/exportxml.cpp
+++ b/src/importexport/musicxml/internal/musicxml/exportxml.cpp
@@ -4393,8 +4396,13 @@ static void wordsMetronome(XmlWriter& xml, Score* s, TextBase const* const text,
         }
         attr += ExportMusicXml::positioningAttributes(text);
         MScoreTextToMXML mttm("words", attr, defFmt, mtf);
-        //LOGD("words('%s')", qPrintable(text->text()));
-        mttm.writeTextFragments(text->fragmentList(), xml);
+        //LOGD("words('%s')", qPrintable(text->xmlText()));
+        if (list.size()) {
+            mttm.writeTextFragments(list, xml);
+        } else {
+            xml.tag("words", text->plainText());
+        }
+
         xml.endElement();
     }

It seems TextBase::fragmentList() (last updated by commit 10ebbb9 on Aug 1, 2018) is missing a fix introduced in the similar function TextBase::plainText() (added by commit 3653f4b on Nov 19, 2018). The fix relayouts a clone of the TextBase when it encounters a TextBase with an invalid layout. With this fix the "Agnus Dei" export seems OK to me. Issue is present in both 3.x and master. I think this would be the structural solution. Can provide a PR for master later.

As an improvement, I suggest TextBase::fragmentList() should be moved to be next to TextBase::plainText() in textbase.cpp, the stress the similarity.

Preliminary fix (plus debug output) in https://github.com/lvinken/MuseScore/tree/293441-direction-type-missing….

Yes, that is correct. Note that I will still be cleaning up my branch (removing unnecessary LOG statements) and adding a testcase, I am not completely done yet.

Status PR created fixed

Fixed in branch master, commit a658fca1f4

_Fix #293441: [MusicXML export] invalid XML export: direction-type is missing child element

TextBase::fragmentList() (last updated by commit 10ebbb9 on Aug 1, 2018) is missing a fix
introduced in the similar function TextBase::plainText() (added by commit 3653f4b on Nov 19, 2018).
The fix relayouts a clone of the TextBase when it encounters a TextBase with an invalid layout._

Fixed in branch master, commit ab0b70d907

_Merge pull request #18056 from Jojo-Schmitz/293441-direction-type-missing-child

Fix #293441: [MusicXML export] invalid XML export: direction-type is missing child element_