Skip to content

Commit ee0f9f0

Browse files
committed
Copyright and page number text styles
1 parent 5eb660c commit ee0f9f0

File tree

70 files changed

+418
-36
lines changed

Some content is hidden

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

70 files changed

+418
-36
lines changed

src/engraving/api/v1/apitypes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ enum class Tid {
558558
INSTRUMENT_CHANGE = int(mu::engraving::TextStyleType::INSTRUMENT_CHANGE),
559559
HEADER = int(mu::engraving::TextStyleType::HEADER),
560560
FOOTER = int(mu::engraving::TextStyleType::FOOTER),
561+
COPYRIGHT = int(mu::engraving::TextStyleType::COPYRIGHT),
562+
PAGE_NUMBER = int(mu::engraving::TextStyleType::PAGE_NUMBER)
561563
};
562564
Q_ENUM_NS(Tid);
563565

src/engraving/dom/page.cpp

Lines changed: 114 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,30 @@ void Page::appendSystem(System* s)
9090

9191
Text* Page::layoutHeaderFooter(int area, const String& ss) const
9292
{
93-
String s = replaceTextMacros(ss);
94-
if (s.isEmpty()) {
93+
bool isHeader = area < MAX_HEADERS;
94+
95+
TextBlock tb = replaceTextMacros(isHeader, ss);
96+
if (tb.fragmentsWithoutEmpty().empty()) {
9597
return nullptr;
9698
}
9799

100+
//! NOTE: Keep in sync with replaceTextMacros
101+
std::wregex copyrightSearch(LR"(\$[cC])");
102+
std::wregex pageNumberSearch(LR"(\$[pPnN])");
103+
bool containsCopyright = ss.contains(copyrightSearch);
104+
bool containsPageNumber = ss.contains(pageNumberSearch);
105+
106+
// Slight hack - we'll use copyright/page number styling if the string contains copyright or page number
107+
// macros (hack because any non-copyright text in the same block will also adopt these style values)
108+
TextStyleType style = containsCopyright ? TextStyleType::COPYRIGHT
109+
: (containsPageNumber ? TextStyleType::PAGE_NUMBER
110+
: (isHeader ? TextStyleType::HEADER : TextStyleType::FOOTER));
111+
98112
Text* text;
99-
if (area < MAX_HEADERS) {
113+
if (isHeader) {
100114
text = score()->headerText(area);
101115
if (!text) {
102-
text = Factory::createText((Page*)this, TextStyleType::HEADER);
116+
text = Factory::createText((Page*)this, style);
103117
text->setFlag(ElementFlag::MOVABLE, false);
104118
text->setFlag(ElementFlag::GENERATED, true); // set to disable editing
105119
text->setLayoutToParentWidth(true);
@@ -108,14 +122,15 @@ Text* Page::layoutHeaderFooter(int area, const String& ss) const
108122
} else {
109123
text = score()->footerText(area - MAX_HEADERS); // because they are 3 4 5
110124
if (!text) {
111-
text = Factory::createText((Page*)this, TextStyleType::FOOTER);
125+
text = Factory::createText((Page*)this, style);
112126
text->setFlag(ElementFlag::MOVABLE, false);
113127
text->setFlag(ElementFlag::GENERATED, true); // set to disable editing
114128
text->setLayoutToParentWidth(true);
115129
score()->setFooterText(text, area - MAX_HEADERS);
116130
}
117131
}
118132
text->setParent((Page*)this);
133+
119134
Align align = { AlignH::LEFT, AlignV::TOP };
120135
switch (area) {
121136
case 0: align = { AlignH::LEFT, AlignV::TOP };
@@ -132,8 +147,14 @@ Text* Page::layoutHeaderFooter(int area, const String& ss) const
132147
break;
133148
}
134149
text->setAlign(align);
135-
text->setXmlText(s);
150+
151+
// Generates text from ldata, ensures newlines are formatted properly...
152+
text->mutldata()->blocks = { tb };
153+
text->genText();
154+
text->createBlocks();
155+
136156
renderer()->layoutItem(text);
157+
137158
return text;
138159
}
139160

@@ -326,10 +347,21 @@ void Page::doRebuildBspTree()
326347
// workTitle
327348
//---------------------------------------------------------
328349

329-
String Page::replaceTextMacros(const String& s) const
350+
TextBlock Page::replaceTextMacros(bool isHeader, const String& s) const
330351
{
331-
String d;
352+
// If the string in question consists solely of a "styled macro" (i.e. page number or copyright), we can set the default
353+
// format to the associated styling. We'll use this later to prevent the creation of unneccessary fragments...
354+
CharFormat defaultFormat = formatForMacro(s);
355+
if (defaultFormat == CharFormat()) {
356+
// The string isn't just a styled macro, use header/footer styling...
357+
defaultFormat.setStyle(style().styleV(isHeader ? Sid::headerFontStyle : Sid::footerFontStyle).value<FontStyle>());
358+
defaultFormat.setFontSize(style().styleD(isHeader ? Sid::headerFontSize : Sid::footerFontSize));
359+
defaultFormat.setFontFamily(style().styleSt(isHeader ? Sid::headerFontFace : Sid::footerFontFace));
360+
}
361+
362+
std::list<TextFragment> fragments(1);
332363
for (size_t i = 0, n = s.size(); i < n; ++i) {
364+
fragments.back().format = defaultFormat;
333365
Char c = s.at(i);
334366
if (c == '$' && (i < (n - 1))) {
335367
Char nc = s.at(i + 1);
@@ -348,52 +380,66 @@ String Page::replaceTextMacros(const String& s) const
348380
{
349381
int no = static_cast<int>(m_no) + 1 + score()->pageNumberOffset();
350382
if (no > 0) {
351-
d += String::number(no);
383+
const String pageNumberString = String::number(no);
384+
const CharFormat pageNumberFormat = formatForMacro(String('$' + nc));
385+
// If the default format equals the format for this macro, we don't need to create a new fragment...
386+
if (defaultFormat == pageNumberFormat) {
387+
fragments.back().text += pageNumberString;
388+
break;
389+
}
390+
TextFragment pageNumberFragment(pageNumberString);
391+
pageNumberFragment.format = pageNumberFormat;
392+
fragments.emplace_back(pageNumberFragment);
393+
fragments.emplace_back(TextFragment()); // Start next fragment
352394
}
353395
}
354396
break;
355397
case 'n':
356-
d += String::number(score()->npages() + score()->pageNumberOffset());
398+
fragments.back().text += String::number(score()->npages() + score()->pageNumberOffset());
357399
break;
358400
case 'i': // not on first page
359401
if (!m_no) {
360402
break;
361403
}
362404
// FALLTHROUGH
363405
case 'I':
364-
d += score()->metaTag(u"partName").toXmlEscaped();
406+
fragments.back().text += score()->metaTag(u"partName").toXmlEscaped();
365407
break;
366408
case 'f':
367-
d += masterScore()->fileInfo()->fileName(false).toString().toXmlEscaped();
409+
fragments.back().text += masterScore()->fileInfo()->fileName(false).toString().toXmlEscaped();
368410
break;
369411
case 'F':
370-
d += masterScore()->fileInfo()->path().toString().toXmlEscaped();
412+
fragments.back().text += masterScore()->fileInfo()->path().toString().toXmlEscaped();
371413
break;
372414
case 'd':
373-
d += muse::Date::currentDate().toString(muse::DateFormat::ISODate);
415+
fragments.back().text += muse::Date::currentDate().toString(muse::DateFormat::ISODate);
374416
break;
375417
case 'D':
376418
{
377419
String creationDate = score()->metaTag(u"creationDate");
378420
if (creationDate.isEmpty()) {
379-
d += masterScore()->fileInfo()->birthTime().date().toString(muse::DateFormat::ISODate);
421+
fragments.back().text += masterScore()->fileInfo()->birthTime().date().toString(
422+
muse::DateFormat::ISODate);
380423
} else {
381-
d += muse::Date::fromStringISOFormat(creationDate).toString(muse::DateFormat::ISODate);
424+
fragments.back().text += muse::Date::fromStringISOFormat(creationDate).toString(
425+
muse::DateFormat::ISODate);
382426
}
383427
}
384428
break;
385429
case 'm':
386430
if (score()->dirty() || !masterScore()->saved()) {
387-
d += muse::Time::currentTime().toString(muse::DateFormat::ISODate);
431+
fragments.back().text += muse::Time::currentTime().toString(muse::DateFormat::ISODate);
388432
} else {
389-
d += masterScore()->fileInfo()->lastModified().time().toString(muse::DateFormat::ISODate);
433+
fragments.back().text += masterScore()->fileInfo()->lastModified().time().toString(
434+
muse::DateFormat::ISODate);
390435
}
391436
break;
392437
case 'M':
393438
if (score()->dirty() || !masterScore()->saved()) {
394-
d += muse::Date::currentDate().toString(muse::DateFormat::ISODate);
439+
fragments.back().text += muse::Date::currentDate().toString(muse::DateFormat::ISODate);
395440
} else {
396-
d += masterScore()->fileInfo()->lastModified().date().toString(muse::DateFormat::ISODate);
441+
fragments.back().text += masterScore()->fileInfo()->lastModified().date().toString(
442+
muse::DateFormat::ISODate);
397443
}
398444
break;
399445
case 'C': // only on first page
@@ -402,29 +448,41 @@ String Page::replaceTextMacros(const String& s) const
402448
}
403449
// FALLTHROUGH
404450
case 'c':
405-
d += score()->metaTag(u"copyright").toXmlEscaped();
406-
break;
451+
{
452+
const String copyrightString = score()->metaTag(u"copyright").toXmlEscaped();
453+
const CharFormat copyrightFormat = formatForMacro(String('$' + nc));
454+
// If the default format equals the format for this macro, we don't need to create a new fragment...
455+
if (defaultFormat == copyrightFormat) {
456+
fragments.back().text += copyrightString;
457+
break;
458+
}
459+
TextFragment copyrightFragment(copyrightString);
460+
copyrightFragment.format = copyrightFormat;
461+
fragments.emplace_back(copyrightFragment);
462+
fragments.emplace_back(TextFragment()); // Start next fragment
463+
}
464+
break;
407465
case 'v':
408466
if (score()->dirty()) {
409-
d += score()->appVersion();
467+
fragments.back().text += score()->appVersion();
410468
} else {
411-
d += score()->mscoreVersion();
469+
fragments.back().text += score()->mscoreVersion();
412470
}
413471
break;
414472
case 'r':
415473
if (score()->dirty()) {
416-
d += revision;
474+
fragments.back().text += revision;
417475
} else {
418476
int rev = score()->mscoreRevision();
419477
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);
478+
fragments.back().text += String::number(rev, 16);
421479
} else {
422-
d += String::number(rev, 10);
480+
fragments.back().text += String::number(rev, 10);
423481
}
424482
}
425483
break;
426484
case '$':
427-
d += '$';
485+
fragments.back().text += '$';
428486
break;
429487
case ':':
430488
{
@@ -437,24 +495,46 @@ String Page::replaceTextMacros(const String& s) const
437495
tag += s.at(k);
438496
}
439497
if (k != n) { // found ':' ?
440-
d += score()->metaTag(tag).toXmlEscaped();
498+
fragments.back().text += score()->metaTag(tag).toXmlEscaped();
441499
i = k - 1;
442500
}
443501
}
444502
break;
445503
default:
446-
d += '$';
447-
d += nc;
504+
fragments.back().text += '$';
505+
fragments.back().text += nc;
448506
break;
449507
}
450508
++i;
451509
} else if (c == '&') {
452-
d += u"&amp;";
510+
fragments.back().text += u"&amp;";
453511
} else {
454-
d += c;
512+
fragments.back().text += c;
455513
}
456514
}
457-
return d;
515+
516+
TextBlock tb;
517+
tb.fragments() = fragments;
518+
return tb;
519+
}
520+
521+
//---------------------------------------------------------
522+
// formatForMacro
523+
//---------------------------------------------------------
524+
525+
const CharFormat Page::formatForMacro(const String& s) const
526+
{
527+
CharFormat format;
528+
if (s == "$c" || s == "$C") {
529+
format.setStyle(style().styleV(Sid::copyrightFontStyle).value<FontStyle>());
530+
format.setFontSize(style().styleD(Sid::copyrightFontSize));
531+
format.setFontFamily(style().styleSt(Sid::copyrightFontFace));
532+
} else if (s == "$p" || s == "$P" || s == "$n" || s == "$N") {
533+
format.setStyle(style().styleV(Sid::pageNumberFontStyle).value<FontStyle>());
534+
format.setFontSize(style().styleD(Sid::pageNumberFontSize));
535+
format.setFontFamily(style().styleSt(Sid::pageNumberFontFace));
536+
}
537+
return format;
458538
}
459539

460540
//---------------------------------------------------------

src/engraving/dom/page.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "engravingitem.h"
2929
#include "bsp.h"
30+
#include "text.h"
3031

3132
namespace mu::engraving {
3233
class RootItem;
@@ -93,7 +94,8 @@ class Page final : public EngravingItem
9394
Page(RootItem* parent);
9495

9596
void doRebuildBspTree();
96-
String replaceTextMacros(const String&) const;
97+
TextBlock replaceTextMacros(bool isHeader, const String&) const;
98+
const CharFormat formatForMacro(const String&) const;
9799

98100
std::vector<System*> m_systems;
99101
page_idx_t m_no = 0; // page number

src/engraving/dom/textbase.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ class TextBase : public EngravingItem
487487
DirectionV direction() const { return m_direction; }
488488
void setCenterBetweenStaves(AutoOnOff v) { m_centerBetweenStaves = v; }
489489
AutoOnOff centerBetweenStaves() const { return m_centerBetweenStaves; }
490+
void genText();
490491

491492
protected:
492493
TextBase(const ElementType& type, EngravingItem* parent = 0, TextStyleType tid = TextStyleType::DEFAULT,
@@ -507,7 +508,7 @@ class TextBase : public EngravingItem
507508
void drawSelection(muse::draw::Painter*, const RectF&) const;
508509
void insert(TextCursor*, char32_t code, LayoutData* ldata) const;
509510
String genText(const LayoutData* ldata) const;
510-
void genText();
511+
511512
virtual int getPropertyFlagsIdx(Pid id) const override;
512513
String stripText(bool, bool, bool) const;
513514
Sid offsetSid() const;

src/engraving/style/styledef.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,38 @@ const std::array<StyleDef::StyleValue, size_t(Sid::STYLES)> StyleDef::styleValue
13151315
styleDef(footerFrameFgColor, PropertyValue::fromValue(Color::BLACK)),
13161316
styleDef(footerFrameBgColor, PropertyValue::fromValue(Color::transparent)),
13171317

1318+
// New for 4.4 - defaults taken from footer
1319+
styleDef(copyrightFontFace, "Edwin"),
1320+
styleDef(copyrightFontSize, 9.0),
1321+
styleDef(copyrightLineSpacing, 1.0),
1322+
styleDef(copyrightFontSpatiumDependent, false),
1323+
styleDef(copyrightFontStyle, int(FontStyle::Normal)),
1324+
styleDef(copyrightColor, PropertyValue::fromValue(Color::BLACK)),
1325+
styleDef(copyrightAlign, Align(AlignH::HCENTER, AlignV::BOTTOM)),
1326+
styleDef(copyrightOffset, PointF(0.0, 0.0)),
1327+
styleDef(copyrightFrameType, int(FrameType::NO_FRAME)),
1328+
styleDef(copyrightFramePadding, 0.2),
1329+
styleDef(copyrightFrameWidth, 0.1),
1330+
styleDef(copyrightFrameRound, 0),
1331+
styleDef(copyrightFrameFgColor, PropertyValue::fromValue(Color::BLACK)),
1332+
styleDef(copyrightFrameBgColor, PropertyValue::fromValue(Color::transparent)),
1333+
1334+
// New for 4.4 - defaults taken from header
1335+
styleDef(pageNumberFontFace, "Edwin"),
1336+
styleDef(pageNumberFontSize, 11.0),
1337+
styleDef(pageNumberLineSpacing, 1.0),
1338+
styleDef(pageNumberFontSpatiumDependent, false),
1339+
styleDef(pageNumberFontStyle, 1),
1340+
styleDef(pageNumberColor, PropertyValue::fromValue(Color::BLACK)),
1341+
styleDef(pageNumberAlign, Align(AlignH::HCENTER, AlignV::TOP)),
1342+
styleDef(pageNumberOffset, PointF()),
1343+
styleDef(pageNumberFrameType, int(FrameType::NO_FRAME)),
1344+
styleDef(pageNumberFramePadding, 0.2),
1345+
styleDef(pageNumberFrameWidth, 0.1),
1346+
styleDef(pageNumberFrameRound, 0),
1347+
styleDef(pageNumberFrameFgColor, PropertyValue::fromValue(Color::BLACK)),
1348+
styleDef(pageNumberFrameBgColor, PropertyValue::fromValue(Color::transparent)),
1349+
13181350
styleDef(instrumentChangeFontFace, "Edwin"),
13191351
styleDef(instrumentChangeFontSize, 10.0),
13201352
styleDef(instrumentChangeLineSpacing, 1.0),

src/engraving/style/styledef.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,36 @@ enum class Sid {
13271327
footerFrameFgColor,
13281328
footerFrameBgColor,
13291329

1330+
copyrightFontFace,
1331+
copyrightFontSize,
1332+
copyrightLineSpacing,
1333+
copyrightFontSpatiumDependent,
1334+
copyrightFontStyle,
1335+
copyrightColor,
1336+
copyrightAlign,
1337+
copyrightOffset,
1338+
copyrightFrameType,
1339+
copyrightFramePadding,
1340+
copyrightFrameWidth,
1341+
copyrightFrameRound,
1342+
copyrightFrameFgColor,
1343+
copyrightFrameBgColor,
1344+
1345+
pageNumberFontFace,
1346+
pageNumberFontSize,
1347+
pageNumberLineSpacing,
1348+
pageNumberFontSpatiumDependent,
1349+
pageNumberFontStyle,
1350+
pageNumberColor,
1351+
pageNumberAlign,
1352+
pageNumberOffset,
1353+
pageNumberFrameType,
1354+
pageNumberFramePadding,
1355+
pageNumberFrameWidth,
1356+
pageNumberFrameRound,
1357+
pageNumberFrameFgColor,
1358+
pageNumberFrameBgColor,
1359+
13301360
instrumentChangeFontFace,
13311361
instrumentChangeFontSize,
13321362
instrumentChangeLineSpacing,

0 commit comments

Comments
 (0)