Skip to content

Commit a818143

Browse files
authored
Merge pull request #28522 from miiizen/19189-xml-newline
Encode line breaks as <br> tags for file saving
2 parents 772de8c + 4f24e3d commit a818143

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+523
-100
lines changed

src/engraving/rw/read460/tread.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3788,6 +3788,12 @@ void TRead::readHopoText(HammerOnPullOffSegment* hopoSeg, XmlReader& xml, ReadCo
37883788
hopoSeg->addHopoText(hopoText);
37893789
}
37903790

3791+
void TRead::lineBreakFromTag(String& str)
3792+
{
3793+
// Raw newlines appearing next to tags (<font size="10> or <sym>...) get eaten by XML readers.
3794+
str.replace(u"<br/>", u"\n");
3795+
}
3796+
37913797
bool TRead::readProperties(Spanner* s, XmlReader& e, ReadContext& ctx)
37923798
{
37933799
const AsciiStringView tag(e.name());
@@ -4416,6 +4422,7 @@ bool TRead::readProperties(TextBase* t, XmlReader& e, ReadContext& ctx)
44164422

44174423
if (tag == "text") {
44184424
String str = e.readXml();
4425+
lineBreakFromTag(str);
44194426
t->setXmlText(str);
44204427
t->checkCustomFormatting(str);
44214428
} else if (tag == "bold") {

src/engraving/rw/read460/tread.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,5 +402,7 @@ class TRead
402402
static void readSystemLock(Score* score, XmlReader& e);
403403

404404
static void readHopoText(HammerOnPullOffSegment* hopoSeg, XmlReader& xml, ReadContext& ctx, int idx);
405+
406+
static void lineBreakFromTag(String& str);
405407
};
406408
}

src/engraving/rw/write/twrite.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,12 @@ void TWrite::writeSystemLock(const SystemLock* systemLock, XmlWriter& xml)
495495
xml.endElement();
496496
}
497497

498+
void TWrite::lineBreakToTag(String& str)
499+
{
500+
// Raw newlines appearing next to tags (<font size="10> or <sym>...) get eaten by XML readers.
501+
str.replace(u"\n", u"<br/>");
502+
}
503+
498504
void TWrite::writeStyledProperties(const EngravingItem* item, XmlWriter& xml)
499505
{
500506
for (const StyledProperty& spp : *item->styledProperties()) {
@@ -1257,7 +1263,9 @@ void TWrite::writeProperties(const TextBase* item, XmlWriter& xml, WriteContext&
12571263
writeProperty(item, xml, spp.pid);
12581264
}
12591265
if (writeText) {
1260-
xml.writeXml(u"text", item->xmlText());
1266+
String xmlStr = item->xmlText();
1267+
lineBreakToTag(xmlStr);
1268+
xml.writeXml(u"text", xmlStr);
12611269
}
12621270

12631271
writeProperty(item, xml, Pid::TEXT_LINKED_TO_MASTER);

src/engraving/rw/write/twrite.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,5 +358,7 @@ class TWrite
358358
static void writeTupletEnd(DurationElement* item, XmlWriter& xml, WriteContext& ctx);
359359

360360
static void writeSystemLock(const SystemLock* systemLock, XmlWriter& xml);
361+
362+
static void lineBreakToTag(String& str);
361363
};
362364
}

src/engraving/rw/xmlreader.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,12 @@ double XmlReader::readDouble(double min, double max)
168168

169169
void XmlReader::htmlToString(int level, String* s)
170170
{
171+
bool selfClosing = noChildren();
171172
*s += u'<' + String::fromAscii(name().ascii());
172173
for (const Attribute& a : attributes()) {
173174
*s += u' ' + String::fromAscii(a.name.ascii()) + u"=\"" + a.value + u'\"';
174175
}
175-
*s += u'>';
176+
*s += selfClosing ? u"/>" : u">";
176177
++level;
177178
for (;;) {
178179
XmlStreamReader::TokenType t = readNext();
@@ -181,7 +182,9 @@ void XmlReader::htmlToString(int level, String* s)
181182
htmlToString(level, s);
182183
break;
183184
case XmlStreamReader::EndElement:
184-
*s += u"</" + String::fromAscii(name().ascii()) + u'>';
185+
if (!selfClosing) {
186+
*s += u"</" + String::fromAscii(name().ascii()) + u'>';
187+
}
185188
--level;
186189
return;
187190
case XmlStreamReader::Characters:

src/engraving/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ set(MODULE_TEST_SRC
9595
${CMAKE_CURRENT_LIST_DIR}/voiceswitching_tests.cpp
9696
${CMAKE_CURRENT_LIST_DIR}/fretbox_tests.cpp
9797
${CMAKE_CURRENT_LIST_DIR}/fretdiagram_tests.cpp
98+
${CMAKE_CURRENT_LIST_DIR}/engraving_xml_tests.cpp
9899

99100
${CMAKE_CURRENT_LIST_DIR}/mocks/engravingconfigurationmock.h
100101
)

src/engraving/tests/copypaste_data/copypaste_parts-ref.mscx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
<tempo>1.333333</tempo>
116116
<followText>1</followText>
117117
<eid>H_H</eid>
118-
<text><sym>metNoteQuarterUp</sym><font face="Edwin"></font> = 80</text>
118+
<text><sym>metNoteQuarterUp</sym><font face="Edwin"/> = 80</text>
119119
</Tempo>
120120
<Spanner type="Pedal">
121121
<Pedal>
@@ -922,7 +922,7 @@
922922
<followText>1</followText>
923923
<eid>EC_EC</eid>
924924
<linkedTo>H_H</linkedTo>
925-
<text><sym>metNoteQuarterUp</sym><font face="Edwin"></font> = 80</text>
925+
<text><sym>metNoteQuarterUp</sym><font face="Edwin"/> = 80</text>
926926
</Tempo>
927927
<Spanner type="Pedal">
928928
<Pedal>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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) 2025 MuseScore Limited
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+
#include <gtest/gtest.h>
24+
#include "engraving/rw/xmlreader.h"
25+
#include "engraving/rw/xmlwriter.h"
26+
#include "thirdparty/kors_logger/src/log_base.h"
27+
#include "types/bytearray.h"
28+
#include "io/buffer.h"
29+
30+
using namespace mu;
31+
using namespace mu::engraving;
32+
using namespace muse;
33+
using namespace muse::io;
34+
35+
class Engraving_XMLTests : public ::testing::Test
36+
{
37+
};
38+
39+
TEST_F(Engraving_XMLTests, readHTML)
40+
{
41+
ByteArray data;
42+
Buffer buf(&data);
43+
static const String XML_TEXT_REF = u"<tag1><br/></tag1>";
44+
static const String XML_TEXT_VERBOSE = u"<tag1><br></br></tag1>";
45+
46+
// Write
47+
{
48+
buf.open(IODevice::WriteOnly);
49+
XmlWriter xml(&buf);
50+
xml.startDocument();
51+
xml.startElement("parent");
52+
53+
xml.writeXml(u"xmlTag", XML_TEXT_REF);
54+
xml.writeXml(u"xmlTag", XML_TEXT_VERBOSE);
55+
56+
xml.endElement();
57+
58+
EXPECT_NE(data.size(), 0);
59+
buf.close();
60+
61+
LOGI() << buf.data().constChar();
62+
}
63+
64+
// Read
65+
{
66+
buf.open(IODevice::ReadOnly);
67+
XmlReader xml(&buf);
68+
69+
EXPECT_TRUE(xml.readNextStartElement());
70+
EXPECT_EQ(xml.name(), "parent");
71+
72+
EXPECT_TRUE(xml.readNextStartElement());
73+
EXPECT_EQ(xml.name(), "xmlTag");
74+
String xmlTag = xml.readXml();
75+
LOGI() << xmlTag;
76+
77+
// Expect that <br></br> is condensed to <br/>
78+
EXPECT_TRUE(xml.readNextStartElement());
79+
EXPECT_EQ(xml.name(), "xmlTag");
80+
String xmlTagVerbose = xml.readXml();
81+
LOGI() << xmlTagVerbose;
82+
EXPECT_EQ(xmlTag, XML_TEXT_REF);
83+
}
84+
}

src/engraving/tests/harpdiagrams_data/textdiagram01-ref.mscx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,7 @@
138138
</pedalState>
139139
<eid>G_G</eid>
140140
<style>harp_pedal_text_diagram</style>
141-
<text>E<sym>csymAccidentalNatural</sym> F<sym>csymAccidentalNatural</sym> G<sym>csymAccidentalNatural</sym> A<sym>csymAccidentalNatural</sym>
142-
D<sym>csymAccidentalNatural</sym> C<sym>csymAccidentalNatural</sym> B<sym>csymAccidentalNatural</sym> </text>
141+
<text>E<sym>csymAccidentalNatural</sym> F<sym>csymAccidentalNatural</sym> G<sym>csymAccidentalNatural</sym> A<sym>csymAccidentalNatural</sym> <br/>D<sym>csymAccidentalNatural</sym> C<sym>csymAccidentalNatural</sym> B<sym>csymAccidentalNatural</sym> </text>
143142
</HarpPedalDiagram>
144143
<Rest>
145144
<eid>H_H</eid>
@@ -164,8 +163,7 @@ D<sym>csymAccidentalNatural</sym> C<sym>csymAccidentalNatural</sym> B<sym>csymAc
164163
</pedalState>
165164
<eid>J_J</eid>
166165
<style>harp_pedal_text_diagram</style>
167-
<text>F<sym>csymAccidentalSharp</sym>
168-
C<sym>csymAccidentalSharp</sym> </text>
166+
<text>F<sym>csymAccidentalSharp</sym> <br/>C<sym>csymAccidentalSharp</sym> </text>
169167
</HarpPedalDiagram>
170168
<Rest>
171169
<eid>K_K</eid>
@@ -190,8 +188,7 @@ C<sym>csymAccidentalSharp</sym> </text>
190188
</pedalState>
191189
<eid>M_M</eid>
192190
<style>harp_pedal_text_diagram</style>
193-
<text>F<sym>csymAccidentalSharp</sym>
194-
C<sym>csymAccidentalSharp</sym> </text>
191+
<text>F<sym>csymAccidentalSharp</sym> <br/>C<sym>csymAccidentalSharp</sym> </text>
195192
</HarpPedalDiagram>
196193
<Rest>
197194
<eid>N_N</eid>
@@ -216,8 +213,7 @@ C<sym>csymAccidentalSharp</sym> </text>
216213
</pedalState>
217214
<eid>P_P</eid>
218215
<style>harp_pedal_text_diagram</style>
219-
<text>G<sym>csymAccidentalSharp</sym>
220-
</text>
216+
<text>G<sym>csymAccidentalSharp</sym> <br/></text>
221217
</HarpPedalDiagram>
222218
<Rest>
223219
<eid>Q_Q</eid>

src/engraving/tests/harpdiagrams_data/textdiagram02.mscx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,7 @@
136136
</pedalState>
137137
<eid>G_G</eid>
138138
<style>harp_pedal_text_diagram</style>
139-
<text>E<sym>csymAccidentalFlat</sym> F<sym>csymAccidentalNatural</sym> G<sym>csymAccidentalNatural</sym> A<sym>csymAccidentalNatural</sym>
140-
D<sym>csymAccidentalNatural</sym> C<sym>csymAccidentalNatural</sym> B<sym>csymAccidentalFlat</sym> </text>
139+
<text>E<sym>csymAccidentalFlat</sym> F<sym>csymAccidentalNatural</sym> G<sym>csymAccidentalNatural</sym> A<sym>csymAccidentalNatural</sym> <br/>D<sym>csymAccidentalNatural</sym> C<sym>csymAccidentalNatural</sym> B<sym>csymAccidentalFlat</sym> </text>
141140
</HarpPedalDiagram>
142141
<Rest>
143142
<eid>H_H</eid>
@@ -172,8 +171,7 @@ D<sym>csymAccidentalNatural</sym> C<sym>csymAccidentalNatural</sym> B<sym>csymAc
172171
</pedalState>
173172
<eid>L_L</eid>
174173
<style>harp_pedal_text_diagram</style>
175-
<text>A<sym>csymAccidentalFlat</sym>
176-
D<sym>csymAccidentalFlat</sym> </text>
174+
<text>A<sym>csymAccidentalFlat</sym> <br/>D<sym>csymAccidentalFlat</sym> </text>
177175
</HarpPedalDiagram>
178176
<Rest>
179177
<eid>M_M</eid>

0 commit comments

Comments
 (0)