@@ -84,19 +84,27 @@ void Page::appendSystem(System* s)
8484 m_systems.push_back (s);
8585}
8686
87+ // Slight hack, see usage (header and footer styles will be getting more holistic rework in future)
88+ #define HEADERFOOTER_SID_FOR_SUFFIX (suffix ) \
89+ (containsCopyright ? Sid::copyright##suffix : \
90+ (containsPageNumber ? Sid::pageNumber##suffix : \
91+ (isHeader ? Sid::header##suffix : Sid::footer##suffix))) \
92+
8793// ---------------------------------------------------------
8894// layoutHeaderFooter
8995// ---------------------------------------------------------
9096
9197Text* Page::layoutHeaderFooter (int area, const String& ss) const
9298{
93- String s = replaceTextMacros (ss);
94- if (s.isEmpty ()) {
99+ bool isHeader = area < MAX_HEADERS;
100+
101+ TextBlock tb = replaceTextMacros (isHeader, ss);
102+ if (tb.fragmentsWithoutEmpty ().empty ()) {
95103 return nullptr ;
96104 }
97105
98106 Text* text;
99- if (area < MAX_HEADERS ) {
107+ if (isHeader ) {
100108 text = score ()->headerText (area);
101109 if (!text) {
102110 text = Factory::createText ((Page*)this , TextStyleType::HEADER);
@@ -116,6 +124,7 @@ Text* Page::layoutHeaderFooter(int area, const String& ss) const
116124 }
117125 }
118126 text->setParent ((Page*)this );
127+
119128 Align align = { AlignH::LEFT, AlignV::TOP };
120129 switch (area) {
121130 case 0 : align = { AlignH::LEFT, AlignV::TOP };
@@ -131,9 +140,42 @@ Text* Page::layoutHeaderFooter(int area, const String& ss) const
131140 case 5 : align = { AlignH::RIGHT, AlignV::BOTTOM };
132141 break ;
133142 }
143+
144+ text->mutldata ()->blocks = { tb };
145+
146+ // ! NOTE: Keep in sync with replaceTextMacros
147+ std::wregex copyrightSearch (LR"( \$[cC])" );
148+ std::wregex pageNumberSearch (LR"( \$[pPnN])" );
149+
150+ bool containsCopyright = ss.contains (copyrightSearch);
151+ bool containsPageNumber = ss.contains (pageNumberSearch);
152+
153+ // TextBase properties (slight hack) - we'll use copyright/page number styling if the string contains
154+ // copyright/page number macros (hack because any non-copyright text will also adopt these style values)
134155 text->setAlign (align);
135- text->setXmlText (s);
156+ text->setTextLineSpacing (style ().styleD (HEADERFOOTER_SID_FOR_SUFFIX (LineSpacing)));
157+ text->setColor (style ().styleV (HEADERFOOTER_SID_FOR_SUFFIX (Color)).value <Color>());
158+
159+ const bool spatiumDependent = style ().styleB (HEADERFOOTER_SID_FOR_SUFFIX (FontSpatiumDependent));
160+ const double factor = spatiumDependent ? SPATIUM20 : DPMM;
161+ const PointF p = style ().styleV (HEADERFOOTER_SID_FOR_SUFFIX (Offset)).value <PointF>();
162+ const PointF pointScaled = { p.x () * factor, p.y () * factor };
163+ text->setOffset (pointScaled);
164+
165+ // Frame properties...
166+ text->setFrameType (style ().styleV (HEADERFOOTER_SID_FOR_SUFFIX (FrameType)).value <FrameType>());
167+ text->setFrameColor (style ().styleV (HEADERFOOTER_SID_FOR_SUFFIX (FrameFgColor)).value <Color>());
168+ text->setBgColor (style ().styleV (HEADERFOOTER_SID_FOR_SUFFIX (FrameBgColor)).value <Color>());
169+ text->setFrameWidth (style ().styleV (HEADERFOOTER_SID_FOR_SUFFIX (FrameWidth)).value <Spatium>());
170+ text->setPaddingWidth (style ().styleV (HEADERFOOTER_SID_FOR_SUFFIX (FramePadding)).value <Spatium>());
171+ text->setFrameRound (style ().styleI (HEADERFOOTER_SID_FOR_SUFFIX (FrameRound)));
172+
173+ // Generates text from ldata, ensures newlines are formatted properly...
174+ text->genText ();
175+ text->createBlocks ();
176+
136177 renderer ()->layoutItem (text);
178+
137179 return text;
138180}
139181
@@ -326,10 +368,14 @@ void Page::doRebuildBspTree()
326368// workTitle
327369// ---------------------------------------------------------
328370
329- String Page::replaceTextMacros (const String& s) const
371+ TextBlock Page::replaceTextMacros (bool isHeader, const String& s) const
330372{
331- String d ;
373+ std::list<TextFragment> fragments ( 1 ) ;
332374 for (size_t i = 0 , n = s.size (); i < n; ++i) {
375+ // Header / footer style by default...
376+ fragments.back ().format .setStyle (style ().styleV (isHeader ? Sid::headerFontStyle : Sid::footerFontStyle).value <FontStyle>());
377+ fragments.back ().format .setFontSize (style ().styleD (isHeader ? Sid::headerFontSize : Sid::footerFontSize));
378+ fragments.back ().format .setFontFamily (style ().styleSt (isHeader ? Sid::headerFontFace : Sid::footerFontFace));
333379 Char c = s.at (i);
334380 if (c == ' $' && (i < (n - 1 ))) {
335381 Char nc = s.at (i + 1 );
@@ -348,52 +394,61 @@ String Page::replaceTextMacros(const String& s) const
348394 {
349395 int no = static_cast <int >(m_no) + 1 + score ()->pageNumberOffset ();
350396 if (no > 0 ) {
351- d += String::number (no);
397+ TextFragment pageNumberFragment (String::number (no));
398+ pageNumberFragment.format .setStyle (style ().styleV (Sid::pageNumberFontStyle).value <FontStyle>());
399+ pageNumberFragment.format .setFontSize (style ().styleD (Sid::pageNumberFontSize));
400+ pageNumberFragment.format .setFontFamily (style ().styleSt (Sid::pageNumberFontFace));
401+ fragments.emplace_back (pageNumberFragment);
402+ fragments.emplace_back (TextFragment ()); // Start next fragment
352403 }
353404 }
354405 break ;
355406 case ' n' :
356- d += String::number (score ()->npages () + score ()->pageNumberOffset ());
407+ fragments. back (). text += String::number (score ()->npages () + score ()->pageNumberOffset ());
357408 break ;
358409 case ' i' : // not on first page
359410 if (!m_no) {
360411 break ;
361412 }
362413 // FALLTHROUGH
363414 case ' I' :
364- d += score ()->metaTag (u" partName" ).toXmlEscaped ();
415+ fragments. back (). text += score ()->metaTag (u" partName" ).toXmlEscaped ();
365416 break ;
366417 case ' f' :
367- d += masterScore ()->fileInfo ()->fileName (false ).toString ().toXmlEscaped ();
418+ fragments. back (). text += masterScore ()->fileInfo ()->fileName (false ).toString ().toXmlEscaped ();
368419 break ;
369420 case ' F' :
370- d += masterScore ()->fileInfo ()->path ().toString ().toXmlEscaped ();
421+ fragments. back (). text += masterScore ()->fileInfo ()->path ().toString ().toXmlEscaped ();
371422 break ;
372423 case ' d' :
373- d += muse::Date::currentDate ().toString (muse::DateFormat::ISODate);
424+ fragments. back (). text += muse::Date::currentDate ().toString (muse::DateFormat::ISODate);
374425 break ;
375426 case ' D' :
376427 {
377428 String creationDate = score ()->metaTag (u" creationDate" );
378429 if (creationDate.isEmpty ()) {
379- d += masterScore ()->fileInfo ()->birthTime ().date ().toString (muse::DateFormat::ISODate);
430+ fragments.back ().text += masterScore ()->fileInfo ()->birthTime ().date ().toString (
431+ muse::DateFormat::ISODate);
380432 } else {
381- d += muse::Date::fromStringISOFormat (creationDate).toString (muse::DateFormat::ISODate);
433+ fragments.back ().text += muse::Date::fromStringISOFormat (creationDate).toString (
434+ muse::DateFormat::ISODate);
382435 }
383436 }
384437 break ;
385438 case ' m' :
386439 if (score ()->dirty () || !masterScore ()->saved ()) {
387- d += muse::Time::currentTime ().toString (muse::DateFormat::ISODate);
440+ fragments. back (). text += muse::Time::currentTime ().toString (muse::DateFormat::ISODate);
388441 } else {
389- d += masterScore ()->fileInfo ()->lastModified ().time ().toString (muse::DateFormat::ISODate);
442+ fragments.back ().text += masterScore ()->fileInfo ()->lastModified ().time ().toString (
443+ muse::DateFormat::ISODate);
390444 }
391445 break ;
392446 case ' M' :
393447 if (score ()->dirty () || !masterScore ()->saved ()) {
394- d += muse::Date::currentDate ().toString (muse::DateFormat::ISODate);
448+ fragments. back (). text += muse::Date::currentDate ().toString (muse::DateFormat::ISODate);
395449 } else {
396- d += masterScore ()->fileInfo ()->lastModified ().date ().toString (muse::DateFormat::ISODate);
450+ fragments.back ().text += masterScore ()->fileInfo ()->lastModified ().date ().toString (
451+ muse::DateFormat::ISODate);
397452 }
398453 break ;
399454 case ' C' : // only on first page
@@ -402,29 +457,36 @@ String Page::replaceTextMacros(const String& s) const
402457 }
403458 // FALLTHROUGH
404459 case ' c' :
405- d += score ()->metaTag (u" copyright" ).toXmlEscaped ();
406- break ;
460+ {
461+ TextFragment copyrightFragment (score ()->metaTag (u" copyright" ).toXmlEscaped ());
462+ copyrightFragment.format .setStyle (style ().styleV (Sid::copyrightFontStyle).value <FontStyle>());
463+ copyrightFragment.format .setFontSize (style ().styleD (Sid::copyrightFontSize));
464+ copyrightFragment.format .setFontFamily (style ().styleSt (Sid::copyrightFontFace));
465+ fragments.emplace_back (copyrightFragment);
466+ fragments.emplace_back (TextFragment ()); // Start next fragment
467+ }
468+ break ;
407469 case ' v' :
408470 if (score ()->dirty ()) {
409- d += score ()->appVersion ();
471+ fragments. back (). text += score ()->appVersion ();
410472 } else {
411- d += score ()->mscoreVersion ();
473+ fragments. back (). text += score ()->mscoreVersion ();
412474 }
413475 break ;
414476 case ' r' :
415477 if (score ()->dirty ()) {
416- d += revision;
478+ fragments. back (). text += revision;
417479 } else {
418480 int rev = score ()->mscoreRevision ();
419481 if (rev > 99999 ) { // MuseScore 1.3 is decimal 5702, 2.0 and later uses a 7-digit hex SHA
420- d += String::number (rev, 16 );
482+ fragments. back (). text += String::number (rev, 16 );
421483 } else {
422- d += String::number (rev, 10 );
484+ fragments. back (). text += String::number (rev, 10 );
423485 }
424486 }
425487 break ;
426488 case ' $' :
427- d += ' $' ;
489+ fragments. back (). text += ' $' ;
428490 break ;
429491 case ' :' :
430492 {
@@ -437,24 +499,27 @@ String Page::replaceTextMacros(const String& s) const
437499 tag += s.at (k);
438500 }
439501 if (k != n) { // found ':' ?
440- d += score ()->metaTag (tag).toXmlEscaped ();
502+ fragments. back (). text += score ()->metaTag (tag).toXmlEscaped ();
441503 i = k - 1 ;
442504 }
443505 }
444506 break ;
445507 default :
446- d += ' $' ;
447- d += nc;
508+ fragments. back (). text += ' $' ;
509+ fragments. back (). text += nc;
448510 break ;
449511 }
450512 ++i;
451513 } else if (c == ' &' ) {
452- d += u" &" ;
514+ fragments. back (). text += u" &" ;
453515 } else {
454- d += c;
516+ fragments. back (). text += c;
455517 }
456518 }
457- return d;
519+
520+ TextBlock tb;
521+ tb.fragments () = fragments;
522+ return tb;
458523}
459524
460525// ---------------------------------------------------------
0 commit comments