diff --git a/abjad/__init__.py b/abjad/__init__.py index 7345d54c5e..e84b07408c 100644 --- a/abjad/__init__.py +++ b/abjad/__init__.py @@ -112,6 +112,7 @@ StopSlur, StopTextSpan, StopTrillSpan, + TextMark, Tie, TimeSignature, VoiceNumber, @@ -466,6 +467,7 @@ "TenorSaxophone", "TenorTrombone", "TenorVoice", + "TextMark", "Tie", "TimeSignature", "Timer", diff --git a/abjad/configuration.py b/abjad/configuration.py index 24d958f11a..9ec9fe5a49 100644 --- a/abjad/configuration.py +++ b/abjad/configuration.py @@ -467,6 +467,7 @@ def list_all_classes(modules="abjad", ignored_classes=None): + diff --git a/abjad/indicators.py b/abjad/indicators.py index 91374bf34f..e02c3f1123 100644 --- a/abjad/indicators.py +++ b/abjad/indicators.py @@ -2987,7 +2987,7 @@ def _get_contributions(self, component=None): @dataclasses.dataclass(frozen=True, order=True, slots=True, unsafe_hash=True) class RehearsalMark: r""" - Rehearsal mark. + LilyPond ``\mark`` command. .. container:: example @@ -5872,6 +5872,112 @@ def _get_contributions(self, component=None): return contributions +@dataclasses.dataclass(frozen=True, order=True, slots=True, unsafe_hash=True) +class TextMark: + r""" + LilyPond ``\textMark``, ``\textEndMark`` commands. + + Text mark: + + .. container:: example + + >>> staff = abjad.Staff("c'4 d' e' f'") + >>> score = abjad.Score([staff]) + >>> mark = abjad.TextMark(r'\textMark \markup \italic "V.S."') + >>> abjad.attach(mark, staff[-1]) + >>> abjad.show(score) # doctest: +SKIP + + .. docs:: + + >>> string = abjad.lilypond(score) + >>> print(string) + \new Score + << + \new Staff + { + c'4 + d'4 + e'4 + \textMark \markup \italic "V.S." + f'4 + } + >> + + Tweaks: + + >>> staff = abjad.Staff("c'4 d' e' f'") + >>> score = abjad.Score([staff]) + >>> mark = abjad.TextMark(r'\textMark \markup \italic "V.S."') + >>> bundle = abjad.bundle(mark, r"\tweak color #blue") + >>> abjad.attach(bundle, staff[-1]) + >>> abjad.show(score) # doctest: +SKIP + + .. docs:: + + >>> string = abjad.lilypond(score) + >>> print(string) + \new Score + << + \new Staff + { + c'4 + d'4 + e'4 + \tweak color #blue + \textMark \markup \italic "V.S." + f'4 + } + >> + + .. container:: example + + Text end mark: + + >>> staff = abjad.Staff("c'4 d' e' f'") + >>> score = abjad.Score([staff]) + >>> mark = abjad.TextMark(r'\textEndMark \markup \italic "V.S."', site="after") + >>> abjad.attach(mark, staff[-1]) + >>> abjad.show(score) # doctest: +SKIP + + .. docs:: + + >>> string = abjad.lilypond(score) + >>> print(string) + \new Score + << + \new Staff + { + c'4 + d'4 + e'4 + f'4 + \textEndMark \markup \italic "V.S." + } + >> + + """ + + string: str + _: dataclasses.KW_ONLY + site: str = "before" + + # find_context_on_attach: typing.ClassVar[bool] = True + context: typing.ClassVar[str] = "Score" + + def __post_init__(self): + assert isinstance(self.string, str), repr(self.string) + + def _get_lilypond_format(self): + return self.string + + def _get_contributions(self, component=None): + contributions = _contributions.ContributionsBySite() + site = getattr(contributions, self.site) + string = self._get_lilypond_format() + site.commands.append(string) + return contributions + + @dataclasses.dataclass(frozen=True, order=True, slots=True, unsafe_hash=True) class Tie: r""" diff --git a/abjad/io.py b/abjad/io.py index f52a101981..c6678a751d 100644 --- a/abjad/io.py +++ b/abjad/io.py @@ -621,7 +621,7 @@ def graph( return format_time, render_time -def show(illustrable, return_timing=False, **keywords): +def show(illustrable, *, flags: str = "", return_timing: bool = False, **keywords): r""" Shows ``argument``. @@ -668,7 +668,7 @@ def show(illustrable, return_timing=False, **keywords): Returns pair of ``abjad_formatting_time`` and ``lilypond_rendering_time`` when ``return_timing`` is true. """ - illustrator = Illustrator(illustrable, **keywords) + illustrator = Illustrator(illustrable, flags=flags.split(), **keywords) result = illustrator() if not result: return diff --git a/abjad/persist.py b/abjad/persist.py index be35fa2ac0..9cfed0e188 100644 --- a/abjad/persist.py +++ b/abjad/persist.py @@ -46,7 +46,7 @@ def as_ly( return ly_file_path, abjad_formatting_time -def as_midi(argument, midi_file_path, **keywords): +def as_midi(argument, midi_file_path, *, flags: str = "", **keywords): """ Persists ``argument`` as MIDI file. @@ -65,7 +65,7 @@ def as_midi(argument, midi_file_path, **keywords): ly_file_path, abjad_formatting_time = as_ly(argument, ly_file_path, **keywords) timer = _contextmanagers.Timer() with timer: - success = _io.run_lilypond(ly_file_path) + success = _io.run_lilypond(ly_file_path, flags=flags) lilypond_rendering_time = timer.elapsed_time if os.name == "nt": extension = "mid" @@ -85,6 +85,7 @@ def as_pdf( argument, pdf_file_path, *, + flags: str = "", illustrate_function=None, tags=False, **keywords, @@ -110,11 +111,12 @@ def as_pdf( **keywords, ) ly_file_path, abjad_formatting_time = result + assert isinstance(ly_file_path, str) without_extension = os.path.splitext(ly_file_path)[0] pdf_file_path = f"{without_extension}.pdf" timer = _contextmanagers.Timer() with timer: - success = _io.run_lilypond(ly_file_path) + success = _io.run_lilypond(ly_file_path, flags=flags) lilypond_rendering_time = timer.elapsed_time return ( pdf_file_path, @@ -128,11 +130,11 @@ def as_png( argument, png_file_path, *, - flags="--png", + flags: str = "--png", illustrate_function=None, - preview=False, + preview: bool = False, resolution=False, - tags=False, + tags: bool = False, **keywords, ): """ @@ -142,6 +144,7 @@ def as_png( Returns output path(s), elapsed formatting time and elapsed rendering time. """ + assert isinstance(flags, str), repr(flags) if png_file_path is not None: png_file_path = os.path.expanduser(png_file_path) without_extension = os.path.splitext(png_file_path)[0] @@ -156,16 +159,17 @@ def as_png( **keywords, ) ly_file_path, abjad_formatting_time = result + assert isinstance(ly_file_path, str) original_directory = os.path.split(ly_file_path)[0] original_ly_file_path = ly_file_path temporary_directory = tempfile.mkdtemp() + assert isinstance(temporary_directory, str) temporary_ly_file_path = os.path.join( temporary_directory, os.path.split(ly_file_path)[1] ) shutil.copy(original_ly_file_path, temporary_ly_file_path) - # render lilypond flags if preview: - flags = "-dpreview" + flags += " -dpreview" if resolution and isinstance(resolution, int): flags += f" -dresolution={resolution}" timer = _contextmanagers.Timer() @@ -183,7 +187,14 @@ def as_png( shutil.rmtree(temporary_directory) if 1 < len(png_file_paths): _png_page_pattern = re.compile(r".+page(\d+)\.png") - png_file_paths.sort(key=lambda x: int(_png_page_pattern.match(x).groups()[0])) + + def _key(path): + match = _png_page_pattern.match(path) + assert match is not None + group = match.groups()[0] + return group + + png_file_paths.sort(key=_key) return ( tuple(png_file_paths), abjad_formatting_time, diff --git a/tests/test_class_design.py b/tests/test_class_design.py index c6a0412a37..ce7854502d 100644 --- a/tests/test_class_design.py +++ b/tests/test_class_design.py @@ -22,6 +22,7 @@ abjad.Markup: (r"\markup Allegro",), abjad.MetricModulation: (abjad.Note("c'4"), abjad.Note("c'4.")), abjad.MetronomeMark: (abjad.Duration(1, 4), 90), + abjad.TextMark: (r'\textMark \markup \italic "V.S."',), abjad.ShortInstrumentName: (r"\markup Vc.",), abjad.StaffChange: ("RH_Staff",), abjad.TimeSignature: ((4, 4),),