[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

2.5 Fonctions pour markups

@translationof Markup functions

Les markups sont implémentés au travers de fonctions Scheme spécifiques qui produisent des objets Stencil comprenant un certain nombre d’arguments.


[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

2.5.1 Construction d’un markup en Scheme

@translationof Markup construction in Scheme

Les expressions markup sont représentées en Scheme de manière interne par la macro markup :

 
(markup expression)

La commande \displayScheme permet d’obtenir la représentation en Scheme d’une expression markup :

 
\displayScheme
\markup {
  \column {
    \line { \bold \italic "hello" \raise #0.4 "world" }
    \larger \line { foo bar baz }
  }
}

Compiler ce code renverra en console les lignes suivantes :

 
(markup
  #:line
  (#:column
   (#:line
    (#:bold (#:italic "hello") #:raise 0.4 "world")
    #:larger
    (#:line
     (#:simple "foo" #:simple "bar" #:simple "baz")))))

L’impression du markup sera suspendue dès lors qu’apparaîtra un ‘\void \displayScheme markup’. Tout comme pour la commande \displayMusic, le résultat de \displayScheme peut être sauvegardé dans un fichier séparé. Voir à ce sujet Affichage d’expressions musicales.

Vous pouvez constater les principales règles de traduction entre les syntaxes respectives de LilyPond et de Scheme en matière de markup. Bien que le passage en syntaxe LilyPond grâce à #{ … #} apporte de la souplesse, nous allons voir comment utiliser la macro markup en Scheme exclusivement.

LilyPondScheme
\markup markup1(markup markup1)
\markup { markup1 markup2… }(markup markup1 markup2… )
\commande-markup#:commande-markup
\variablevariable
\center-column { … }#:center-column ( … )
chaîne"chaîne"
#argument-schemeargument-scheme

L’intégralité du langage Scheme est accessible à l’intérieur même de la macro markup. Vous pouvez ainsi appeler des fonctions à partir de markup pour manipuler des chaînes de caractères, ce qui est particulièrement pratique lorsque vous créez votre propre commande de markup – voir Définition d’une nouvelle commande de markup.

Problèmes connus et avertissements

L’argument markup-list des commandes #:line, #:center ou #:column ne saurait être une variable ni le résultat de l’appel à une fonction.

 
(markup #:line (fonction-qui-retourne-des-markups))

n’est pas valide. Il vaut mieux, en pareil cas, utiliser les fonctions make-line-markup, make-center-markup ou make-column-markup :

 
(markup (make-line-markup (fonction-qui-retourne-des-markups)))

[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

2.5.2 Fonctionnement interne des markups

@translationof How markups work internally

Dans un markup tel que

 
\raise #0.5 "text example"

\raise représente en fait la fonction raise-markup. L’expression markup est enregistrée sous la forme

 
(list raise-markup 0.5 (list simple-markup "text example"))

Lorsque ce markup est converti en objets imprimables (stencils), la fonction raise-markup est appelée ainsi :

 
(apply raise-markup
       \layout objet
       liste des alists de propriété
       0.5
       le markup "text example")

La fonction raise-markup commence par créer le stencil pour la chaîne text example, puis remonte ce stencil d’un demi espace de portée. Il s’agit là d’un exemple relativement simple, et nous en aborderons de plus complexes au fil des paragraphes suivants ; d’autres exemples se trouvent directement dans le fichier ‘scm/define-markup-commands.scm’.


[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

2.5.3 Définition d’une nouvelle commande de markup

@translationof New markup command definition

Nous allons étudier dans ce qui suit la manière de définir une nouvelle commande de markup.


[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

Syntaxe d’une commande markup

@translationof Markup command definition syntax

Une commande de markup personnalisée se définit à l’aide de la macro Scheme define-markup-command, placée en tête de fichier.

 
(define-markup-command (nom-commande layout props arg1 arg2…)
    (arg1-type? arg2-type?…)
    [ #:properties ((propriété1 valeur-par-défaut1)
                    …) ]
  …corps de la commande…)

Quelques commentaires sur les arguments :

nom-commande

le nom que vous attribuez à votre commande de markup.

layout

la définition du « layout » – son formatage.

props

une liste de listes associatives, comprenant toutes les propriétés actives.

argi

le ième argument de la commande.

argi-type?

un type de prédicat pour le ième argument.

Si la commande utilise des propriétés à partir des arguments props, le mot-clé #:properties permet de spécifier ces différentes propriétés ainsi que leur valeur par défaut.

Les arguments se distinguent selon leur type :

Il n’existe aucune restriction quant à l’ordre des arguments fournis à la suite des arguments layout et props. Néanmoins, les fonctions markup qui ont en dernier argument un markup ont ceci de particulier qu’elles peuvent s’appliquer à des listes de markups ; ceci résultera en une liste de markups où tous les éléments de la liste originelle se verront appliquer cette fonction markup avec ses arguments de tête.

La réplication des arguments de tête dans le but d’appliquer une fonction markup à une liste de markups est économique, principalement lorsqu’il s’agit d’arguments Scheme. Vous éviterez ainsi d’éventuelles pertes de performance en utilisant des arguments Scheme en tant qu’arguments principaux d’une fonction markup dont le dernier argument est un markup.

Les commandes de markup ont un cycle de vie relativement complexe. Le corps de la définition d’une commande de markup est chargé de convertir les arguments de la commande en expression stencil qui sera alors renvoyée. Bien souvent, ceci s’accomplit par un appel à la fonction interpret-markup, en lui passant les arguments layout et props. Ces arguments ne seront en principe connus que bien plus tardivement dans le processus typographique. Lors de l’expansion d’une expression LilyPond \markup ou d’une macro Scheme markup, les expressions markup auront déjà vu leurs composants assemblés en expressions markup. L’évaluation et le contrôle du type des arguments à une commande de markup n’interviennent qu’au moment de l’interprétation de \markup ou markup.

Seule l’application de interpret-markup sur une expression markup réalisera effectivement la conversion des expressions markup en stencil, au travers de l’exécution du corps des fonctions markup.


[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

Attribution de propriétés

@translationof On properties

Les arguments layout et props d’une commande de markup fournissent un contexte à l’interprétation du markup : taille de fonte, longueur de ligne, etc.

L’argument layout permet d’accéder aux propriétés définies dans les blocs \paper, grâce à la fonction ly:output-def-lookup. Par exemple, la longueur de ligne, identique à celle de la partition, est lue au travers de

 
(ly:output-def-lookup layout 'line-width)

L’argument props rend certaines propriétés accessibles aux commandes de markup. Il en va ainsi lors de l’interprétation d’un markup de titre d’ouvrage : toutes les variables définies dans le bloc \header sont automatiquement ajoutées aux props, de telle sorte que le markup de titrage de l’ouvrage pourra accéder aux différents champs titre, compositeur, etc. Ceci permet aussi de configurer le comportement d’une commande de markup : la taille des fontes, par exemple, est lue à partir de props plutôt que grâce à un argument font-size. La fonction appelant une commande de markup peut altérer la valeur de la propriété taille des fontes et donc en modifier le comportement. L’utilisation du mot-clé #:properties, attaché à define-markup-command, permet de spécifier les propriétés devant être lues parmi les arguments props.

L’exemple proposé à la rubrique suivante illustre comment, au sein d’une commande de markup, accéder aux différentes propriétés et les modifier.


[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

Exemple commenté

@translationof A complete example

Nous allons, dans cet exemple, nous attacher à encadrer du texte avec un double liseré.

Commençons par construire quelque chose d’approximatif à l’aide d’un simple markup. La lecture de (notation)Commandes pour markup section ‘Commandes pour markup’ dans Manuel de notation nous indique la commande \box, qui semble ici appropriée.

 
\markup \box \box HELLO

[image of music]

Dans un souci d’esthétique, nous aimerions que le texte et les encadrements ne soient pas autant accolés. Selon la documentation de \box, cette commande utilise la propriété box-padding, fixée par défaut à 0,2. Cette même documentation nous indique aussi comment la modifier :

 
\markup \box \override #'(box-padding . 0.6) \box A

[image of music]

L’espacement des deux liserés est cependant toujours trop réduit ; modifions le à son tour :

 
\markup \override #'(box-padding . 0.4) \box
     \override #'(box-padding . 0.6) \box A

[image of music]

Vous conviendrez que recopier une telle définition de markup deviendra vite fastidieux. C’est pourquoi nous écrivons la commande de markup double-box qui prendra un seul argument – le texte. Cette commande se chargera de dessiner les encadrements, en tenant compte des espacements.

 
#(define-markup-command (double-box layout props text) (markup?)
  "Dessine un double encadrement autour du texte."
  (interpret-markup layout props
    #{\markup \override #'(box-padding . 0.4) \box
            \override #'(box-padding . 0.6) \box { #text }#}))

ou bien son équivalent

 
#(define-markup-command (double-box layout props text) (markup?)
  "Dessine un double encadrement autour du texte."
  (interpret-markup layout props
    (markup #:override '(box-padding . 0.4) #:box
            #:override '(box-padding . 0.6) #:box text)))

text est le nom de l’argument de notre commande, et markup? son type – l’argument sera identifié comme étant un markup. La fonction interpret-markup, utilisée dans la plupart des commandes de markup, construira un stencil à partir de layout, props et un markup. Dans la seconde variante, ce markup sera construit à l’aide de la macro Scheme markup – voir Construction d’un markup en Scheme. La transformation d’une expression \markup en expression Scheme est des plus triviales.

Notre commande personnalisée s’utilise ainsi :

 
\markup \double-box A

Il serait intéressant de rendre cette commande double-box plus souple : les valeurs de box-padding sont figées et ne peuvent être modifiées à l’envie. Pareillement, il serait bien de distinguer l’espacement entre les encadrements de l’espacement entre le texte et ses encadrements. Nous allons donc introduire une propriété supplémentaire, que nous appellerons inter-box-padding, chargée de gérer l’espacement des encadrements ; box-padding ne servira alors que pour l’espacement intérieur. Voici le code adapté à ces évolutions :

 
#(define-markup-command (double-box layout props text) (markup?)
  #:properties ((inter-box-padding 0.4)
                (box-padding 0.6))
  "Dessine un double encadrement autour du texte."
  (interpret-markup layout props
    #{\markup \override #`(box-padding . ,inter-box-padding) \box
               \override #`(box-padding . ,box-padding) \box
               { #text } #}))

Ainsi que son équivalent à partir de la macro markup :

 
#(define-markup-command (double-box layout props text) (markup?)
  #:properties ((inter-box-padding 0.4)
                (box-padding 0.6))
  "Dessine un double encadrement autour du texte."
  (interpret-markup layout props
    (markup #:override `(box-padding . ,inter-box-padding) #:box
            #:override `(box-padding . ,box-padding) #:box text)))

C’est ici le mot-clé #:properties qui permet de lire les propriétés inter-box-padding et box-padding à partir de l’argumenet props ; on leur a d’ailleurs fourni des valeurs par défaut au cas où elles ne seraient pas définies.

Ces valeurs permettront alors d’adapter les propriétés de box-padding utilisées par les deux commandes \box. Vous aurez remarqué, dans l’argument \override, la présence de l’apostrophe inversée (`) et de la virgule ; elles vous permettent d’insérer une valeur variable au sein d’une expression littérale.

Notre commande est maintenant prête à servir dans un markup, et les encadrements sont repositionnables.

 
#(define-markup-command (double-box layout props text) (markup?)
  #:properties ((inter-box-padding 0.4)
                (box-padding 0.6))
  "Draw a double box around text."
  (interpret-markup layout props
    #{\markup \override #`(box-padding . ,inter-box-padding) \box
              \override #`(box-padding . ,box-padding) \box
              { #text } #}))

\markup \double-box A
\markup \override #'(inter-box-padding . 0.8) \double-box A
\markup \override #'(box-padding . 1.0) \double-box A

[image of music]


[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

Adaptation d’une commande incorporée

@translationof Adapting builtin commands

Le meilleur moyen de construire ses propres commandes de markup consiste à prendre exemple sur les commandes déjà incorporées. La plupart des commandes de markup fournies avec LilyPond sont répertoriées dans le fichier ‘scm/define-markup-commands.scm’.

Nous pourrions, par exemple, envisager d’adapter la commande \draw-line pour dessiner plutôt une ligne double. Voici comment est définie la commande \draw-line, expurgée de sa documentation :

 
(define-markup-command (draw-line layout props dest)
  (number-pair?)
  #:category graphic
  #:properties ((thickness 1))
  "…documentation…"
  (let ((th (* (ly:output-def-lookup layout 'line-thickness)
               thickness))
        (x (car dest))
        (y (cdr dest)))
    (make-line-stencil th 0 0 x y)))

Avant de définir notre propre commande basée sur l’une de celles fournies par LilyPond, commençons par en recopier la définition, puis attribuons lui un autre nom. Le mot-clé #:category peut être supprimé sans risque ; il ne sert que lors de la génération de la documentation et n’est d’aucune utilité pour une commande personnalisée.

 
(define-markup-command (draw-double-line layout props dest)
  (number-pair?)
  #:properties ((thickness 1))
  "…documentation…"
  (let ((th (* (ly:output-def-lookup layout 'line-thickness)
               thickness))
        (x (car dest))
        (y (cdr dest)))
    (make-line-stencil th 0 0 x y)))

Nous ajoutons ensuite une propriété pour gérer l’écart entre les deux lignes, que nous appelons line-gap, et lui attribuons une valeur par défaut de 6 dixièmes :

 
(define-markup-command (draw-double-line layout props dest)
  (number-pair?)
  #:properties ((thickness 1)
                (line-gap 0.6))
  "…documentation…"
  …

Nous ajoutons enfin le code qui dessinera nos deux lignes. Deux appels à make-line-stencil permettrons de dessiner les lignes dont nous regrouperons les stencils à l’aide de ly:stencil-add :

 
#(define-markup-command (my-draw-line layout props dest)
  (number-pair?)
  #:properties ((thickness 1)
                (line-gap 0.6))
  "..documentation.."
  (let* ((th (* (ly:output-def-lookup layout 'line-thickness)
                thickness))
         (dx (car dest))
         (dy (cdr dest))
         (w (/ line-gap 2.0))
         (x (cond ((= dx 0) w)
                  ((= dy 0) 0)
                  (else (/ w (sqrt (+ 1 (* (/ dx dy) (/ dx dy))))))))
         (y (* (if (< (* dx dy) 0) 1 -1)
               (cond ((= dy 0) w)
                     ((= dx 0) 0)
                     (else (/ w (sqrt (+ 1 (* (/ dy dx) (/ dy dx))))))))))
     (ly:stencil-add (make-line-stencil th x y (+ dx x) (+ dy y))
                     (make-line-stencil th (- x) (- y) (- dx x) (- dy y)))))

\markup \my-draw-line #'(4 . 3)
\markup \override #'(line-gap . 1.2) \my-draw-line #'(4 . 3)

[image of music]


[ < ] [ > ]   [ << ] [Plus haut] [ >> ]         [Racine] [Table des matières] [Index] [ ? ]

2.5.4 Définition d’une nouvelle commande de liste de markups

@translationof New markup list command definition

Une commande traitant une liste de markups se définit à l’aide de la macro Scheme define-markup-list-command, de manière analogue à la macro define-markup-command abordée à la rubrique Définition d’une nouvelle commande de markup, à ceci près que cette dernière renvoie un seul stencil, non une liste de stencils.

La fonction interpret-markup-list, à l’instar de la fonction interpret-markup, permet de convertir une liste de markups en liste de stencils.

Dans l’exemple suivant, nous définissons \paragraph, une commande de liste de markups, qui renverra une liste de lignes justifiées dont la première sera indentée. La largeur de l’alinéa sera récupérée par l’argument props.

 
#(define-markup-list-command (paragraph layout props args) (markup-list?)
   #:properties ((par-indent 2))
   (interpret-markup-list layout props
     #{\markuplist \justified-lines { \hspace #par-indent #args } #}))

La version purement Scheme est un peu plus complexe :

 
#(define-markup-list-command (paragraph layout props args) (markup-list?)
   #:properties ((par-indent 2))
   (interpret-markup-list layout props
     (make-justified-lines-markup-list (cons (make-hspace-markup par-indent)
                                             args))))

En dehors des habituels arguments layout et props, la commande de liste de markups paragraph prend en argument une liste de markups appelée args. Le prédicat des listes de markups est markup-list?.

Pour commencer, la fonction récupère la taille de l’alinéa, propriété ici dénommée par-indent, à partir de la liste de propriétés props. En cas d’absence, la valeur par défaut sera de 2. Ensuite est créée une liste de lignes justifiées grâce à la commande prédéfinie \justified-lines, liée à la fonction make-justified-lines-markup-list. Un espace horizontal est ajouté en tête, grâce à \hspace ou à la fonction make-hspace-markup. Enfin, la liste de markups est interprétée par la fonction interpret-markup-list.

Voici comment utiliser cette nouvelle commande de liste de markups :

 
\markuplist {
  \paragraph {
    The art of music typography is called \italic {(plate) engraving.}
    The term derives from the traditional process of music printing.
    Just a few decades ago, sheet music was made by cutting and stamping
    the music into a zinc or pewter plate in mirror image.
  }
  \override-lines #'(par-indent . 4) \paragraph {
    The plate would be inked, the depressions caused by the cutting
    and stamping would hold ink.  An image was formed by pressing paper
    to the plate.  The stamping and cutting was completely done by
    hand.
  }
}

[ < ] [ > ]   [ << ] [Plus haut] [ >> ]

Ce document a été généré le le 13 juillet 2020 par gub en utilisant texi2html 1.82.

Autres langues : English, deutsch, español.
À propos de la sélection automatique de la langue.