diff --git a/include/vrv/note.h b/include/vrv/note.h index c27fb7ac026..6e90dfa0c61 100644 --- a/include/vrv/note.h +++ b/include/vrv/note.h @@ -166,7 +166,7 @@ class Note : public LayerElement, * @name Return the smufl string to use for a note give the notation type */ ///@{ - std::u32string GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike) const; + std::u32string GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike, int &underline) const; ///@} /** diff --git a/src/note.cpp b/src/note.cpp index 433300820b7..cdf254cc17d 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -224,6 +224,17 @@ bool Note::HasLedgerLines(int &linesAbove, int &linesBelow, const Staff *staff) staff = this->GetAncestorStaff(); } + if (staff->IsTabLuteFrench() || staff->IsTabLuteGerman() || staff->IsTabLuteItalian()) { + // French and German tablature do not use ledger lines. + // Italian tablature does use a single ledger line for 7th course, and compressed + // ledger lines for fretted 8th and above, but not for open 8th and above. So + // rather than use the CMN ledger line handling we draw our own. + // Guitar tablature has been left as originally implemented. + linesAbove = 0; + linesBelow = 0; + return false; + } + linesAbove = (this->GetDrawingLoc() - staff->m_drawingLines * 2 + 2) / 2; linesBelow = -(this->GetDrawingLoc()) / 2; @@ -271,10 +282,11 @@ const TabGrp *Note::IsTabGrpNote() const return vrv_cast(this->GetFirstAncestor(TABGRP, MAX_TABGRP_DEPTH)); } -std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike) const +std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, int &overline, int &strike, int &underline) const { overline = 0; strike = 0; + underline = 0; // @glyph.num, @glyph.name or @altsym const Resources *resources = this->GetDocResources(); @@ -321,21 +333,27 @@ std::u32string Note::GetTabFretString(data_NOTATIONTYPE notationType, int &overl if (notationType == NOTATIONTYPE_tab_lute_italian) { std::u32string fretStr; - int fret = this->GetTabFret(); - // Maximum allowed would be 19 (always bindly adding 1 as first figure) - if (fret > 9) fretStr.push_back(SMUFL_EBE1_luteItalianFret1); - switch (fret % 10) { - case 0: fretStr.push_back(SMUFL_EBE0_luteItalianFret0); break; - case 1: fretStr.push_back(SMUFL_EBE1_luteItalianFret1); break; - case 2: fretStr.push_back(SMUFL_EBE2_luteItalianFret2); break; - case 3: fretStr.push_back(SMUFL_EBE3_luteItalianFret3); break; - case 4: fretStr.push_back(SMUFL_EBE4_luteItalianFret4); break; - case 5: fretStr.push_back(SMUFL_EBE5_luteItalianFret5); break; - case 6: fretStr.push_back(SMUFL_EBE6_luteItalianFret6); break; - case 7: fretStr.push_back(SMUFL_EBE7_luteItalianFret7); break; - case 8: fretStr.push_back(SMUFL_EBE8_luteItalianFret8); break; - case 9: fretStr.push_back(SMUFL_EBE9_luteItalianFret9); break; - default: break; + const int fret = this->GetTabFret(); + const int course = this->GetTabCourse(); + + // Italian tablature glyphs are contiguous + static_assert(SMUFL_EBE1_luteItalianFret1 == SMUFL_EBE0_luteItalianFret0 + 1); + static_assert(SMUFL_EBE2_luteItalianFret2 == SMUFL_EBE0_luteItalianFret0 + 2); + // ... + static_assert(SMUFL_EBE9_luteItalianFret9 == SMUFL_EBE0_luteItalianFret0 + 9); + + if (course <= 7 || fret != 0) { + const auto decimal = std::div(fret, 10); + if (decimal.quot > 0) fretStr.push_back(SMUFL_EBE0_luteItalianFret0 + decimal.quot); + if (decimal.rem <= 9) fretStr.push_back(SMUFL_EBE0_luteItalianFret0 + decimal.rem); + if (course >= 7) strike = 1; // course 7 and fretted courses >= 8 use a ledger line + underline = std::max(0, course - 7); // compressed ledger lines for fretted courses >= 8 + } + else { + // open courses >= 8 just use the number of the course on stave line 7 + const auto decimal = std::div(course, 10); + if (decimal.quot > 0) fretStr.push_back(SMUFL_EBE0_luteItalianFret0 + decimal.quot); + if (decimal.rem <= 9) fretStr.push_back(SMUFL_EBE0_luteItalianFret0 + decimal.rem); } return fretStr; } diff --git a/src/tuning.cpp b/src/tuning.cpp index c7f71fa3f3d..1dde76662fc 100644 --- a/src/tuning.cpp +++ b/src/tuning.cpp @@ -63,7 +63,9 @@ int Tuning::CalcPitchPos( case NOTATIONTYPE_tab_lute_french: // all courses >= 7 are positioned above line 0 return (lines - std::min(course, 7)) * 2 + 1; // above the line - case NOTATIONTYPE_tab_lute_italian: return (course - 1) * 2; + case NOTATIONTYPE_tab_lute_italian: + // all courses >= 7 are positioned on line 7 + return (std::min(course, 7) - 1) * 2; case NOTATIONTYPE_tab_lute_german: if (loc != MEI_UNSET) { return loc; diff --git a/src/view_tab.cpp b/src/view_tab.cpp index 9e82e4441b8..44a247351b6 100644 --- a/src/view_tab.cpp +++ b/src/view_tab.cpp @@ -106,10 +106,11 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S bool drawingCueSize = false; int overline = 0; int strike = 0; + int underline = 0; if (staff->m_drawingNotationType == NOTATIONTYPE_tab_guitar) { - std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike); + std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike, underline); FontInfo fretTxt; if (!dc->UseGlobalStyling()) { @@ -135,7 +136,7 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S } else { - std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike); + std::u32string fret = note->GetTabFretString(staff->m_drawingNotationType, overline, strike, underline); // Center for italian tablature if (staff->IsTabLuteItalian()) { y -= (m_doc->GetGlyphHeight(SMUFL_EBE0_luteItalianFret0, glyphSize, drawingCueSize) / 2); @@ -153,8 +154,8 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S dc->SetFont(m_doc->GetDrawingSmuflFont(glyphSize, false)); this->DrawSmuflString(dc, x, y, fret, HORIZONTALALIGNMENT_center, glyphSize); - // Add overlines or strikethoughs if required - if ((overline > 0 || strike > 0) && !fret.empty()) { + // Add overlines, strikethoughs and underlines if required + if ((overline > 0 || strike > 0 || underline > 0) && !fret.empty()) { const int lineThickness = m_options->m_lyricLineThickness.GetValue() * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); const int widthFront = m_doc->GetGlyphWidth(fret.front(), glyphSize, drawingCueSize); @@ -162,8 +163,11 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S TextExtend extend; dc->GetSmuflTextExtent(fret, &extend); - const int x1 = x - widthFront / 2; - const int x2 = x + extend.m_width - widthBack * 3 / 10; // trim right hand overhang on last character + // TODO These fiddle factors seem necessary to get the lines balanced on either side + // of the fret string. Can we do better? + const int x1 + = x - (fret.size() == 1 ? widthFront * 7 / 10 : widthFront * 12 / 10); // extend on the left hand side + const int x2 = x + extend.m_width - widthBack * 1 / 10; // trim right hand overhang on last character dc->SetPen(m_currentColor, lineThickness, AxSOLID); dc->SetBrush(m_currentColor, AxSOLID); @@ -184,6 +188,14 @@ void View::DrawTabNote(DeviceContext *dc, LayerElement *element, Layer *layer, S y1 += 2 * lineThickness; } + // underlines + y1 = y - extend.m_descent - lineThickness; + + for (int i = 0; i < underline; ++i) { + dc->DrawLine(ToDeviceContextX(x1), ToDeviceContextY(y1), ToDeviceContextX(x2), ToDeviceContextY(y1)); + y1 -= 2 * lineThickness; + } + dc->ResetPen(); dc->ResetBrush(); } @@ -214,6 +226,12 @@ void View::DrawTabDurSym(DeviceContext *dc, LayerElement *element, Layer *layer, * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); tabDurSym->SetDrawingYRel(-yRel); } + else if (staff->IsTabLuteItalian()) { + // make space for 7th course + const int yRel + = ((staff->m_drawingLines - 1) * 2 - 7 * 2 + 1) * m_doc->GetDrawingUnit(staff->m_drawingStaffSize); + tabDurSym->SetDrawingYRel(-yRel); + } int x = element->GetDrawingX(); int y = element->GetDrawingY();