X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Documentation%2Fes%2Fuser%2Fprogramming-interface.itely;h=f9ee790124feb0a3834fcbadfe06621628c8cf8f;hb=0387f04497978e37b335a8b99eec905499d6ad0f;hp=1946ae40a7776db11034a2a3c008ca01e658d6af;hpb=edf17353d89f4f6bd831466262402bb9151a26ca;p=lilypond.git diff --git a/Documentation/es/user/programming-interface.itely b/Documentation/es/user/programming-interface.itely index 1946ae40a7..f9ee790124 100644 --- a/Documentation/es/user/programming-interface.itely +++ b/Documentation/es/user/programming-interface.itely @@ -1,34 +1,40 @@ @c -*- coding: utf-8; mode: texinfo; documentlanguage: es -*- @c This file is part of lilypond.tely @ignore - Translation of GIT committish: 66dde21fe63499f32a718f6098abe70e1429059b + Translation of GIT committish: 0646758d26f727fc27fc13a87df2362388909e5d When revising a translation, copy the HEAD committish of the version that you are working on. See TRANSLATION for details. @end ignore -@c \version "2.11.51" - +@c \version "2.11.61" @node Interfaces for programmers @chapter Interfaces for programmers -UNTRANSLATED NODE: IGNORE ME +Se pueden realizar trucos avanzados mediante el uso de Scheme. Si no +está familizarizado con Scheme, le conviene leer nuestro tutorial de +Scheme, @rlearning{Scheme tutorial}. -@menu +@menu * Music functions:: * Programmer interfaces:: * Building complicated functions:: * Markup programmer interface:: * Contexts for programmers:: * Scheme procedures as properties:: -@end menu +* Using Scheme code instead of \tweak:: +* Difficult tweaks:: +@end menu + + @node Music functions @section Music functions -UNTRANSLATED NODE: IGNORE ME +Esta sección trata sobre cómo crear funciones musicales dentro de +LilyPond. -@menu +@menu * Overview of music functions:: * Simple substitution functions:: * Paired substitution functions:: @@ -36,149 +42,1489 @@ UNTRANSLATED NODE: IGNORE ME * Void functions:: * Functions without arguments:: * Overview of available music functions:: -@end menu +@end menu + @node Overview of music functions @subsection Overview of music functions -UNTRANSLATED NODE: IGNORE ME +Es fácil hacer una función que sustituya a una variable en código de +LilyPond. La forma general de estas funciones es: + +@example +function = +#(define-music-function (parser location @var{var1} @var{var2}...@var{vari}... ) + (@var{var1-type?} @var{var2-type?}...@var{vari-type?}...) + #@{ + @emph{...música...} + #@}) +@end example + +@noindent +donde + +@multitable @columnfractions .33 .66 +@item @var{vari} @tab @var{i}-ésima variable +@item @var{vari-type?} @tab tipo de la @var{i}-ésima variable +@item @var{...música...} @tab entrada normal de LilyPond, usando las variables como @code{#$var1}, etc. +@end multitable + +Los siguientes tipos de entrada se pueden usar como variables en una +función musical. Esta lista no es exhaustiva; consulte otros lugares +de la documentación específica de Scheme para ver otros tipos de +variables. + +@multitable @columnfractions .33 .66 +@headitem Tipo de entrada @tab notación de @var{vari-type?} +@item Entero @tab @code{integer?} +@item Flotante (número decimal) @tab @code{number?} +@item Cadena de texto @tab @code{string?} +@item Marcado @tab @code{markup?} +@item Expresión musical @tab @code{ly:music?} +@item Pareja de variables @tab @code{pair?} +@end multitable + +Los argumentos @code{parser} y @code{location} son obligatorios, y se +usan en ciertas situaciones avanzadas. El argumento @code{parser} se +usa para tener acceso al valor de otra variable de LilyPond. El +argumento @code{location} se usa para establecer el @q{origen} de la +expresión musical que construye la función musical, de forma que en +caso de producirse un error de sintaxis LilyPond pueda informar al +usuario de un lugar adecuado donde buscar en el archivo de entrada. + @node Simple substitution functions @subsection Simple substitution functions -UNTRANSLATED NODE: IGNORE ME +He aquí un ejemplo sencillo: + +@lilypond[quote,verbatim,ragged-right] +padText = #(define-music-function (parser location padding) (number?) + #{ + \once \override TextScript #'padding = #$padding + #}) + +\relative c''' { + c4^"piu mosso" b a b + \padText #1.8 + c4^"piu mosso" d e f + \padText #2.6 + c4^"piu mosso" fis a g +} +@end lilypond + +También se pueden sustituir las expresiones musicales: + +@lilypond[quote,verbatim,ragged-right] +custosNote = #(define-music-function (parser location note) + (ly:music?) + #{ + \once \override Voice.NoteHead #'stencil = + #ly:text-interface::print + \once \override Voice.NoteHead #'text = + \markup \musicglyph #"custodes.mensural.u0" + \once \override Voice.Stem #'stencil = ##f + $note + #}) + +{ c' d' e' f' \custosNote g' } +@end lilypond + +Se pueden usar más de una variable: + +@lilypond[quote,verbatim,ragged-right] +tempoMark = #(define-music-function (parser location padding marktext) + (number? string?) +#{ + \once \override Score . RehearsalMark #'padding = $padding + \once \override Score . RehearsalMark #'extra-spacing-width = #'(+inf.0 . -inf.0) + \mark \markup { \bold $marktext } +#}) + +\relative c'' { +c2 e +\tempoMark #3.0 #"Allegro" +g c +} +@end lilypond + @node Paired substitution functions @subsection Paired substitution functions -UNTRANSLATED NODE: IGNORE ME +Algunas instrucciones @code{\override} requieren un par de números +(llamados en Scheme una @code{célula cons}). Para pasar estos números +a una función, usamos una variable @code{pair?} o bien insertamos el +@code{cons} en la función musical. + +@quotation +@example +manualBeam = +#(define-music-function (parser location beg-end) + (pair?) +#@{ + \once \override Beam #'positions = #$beg-end +#@}) + +\relative @{ + \manualBeam #'(3 . 6) c8 d e f +@} +@end example +@end quotation + +@noindent +o bien + +@lilypond[quote,verbatim,ragged-right] +manualBeam = +#(define-music-function (parser location beg end) + (number? number?) +#{ + \once \override Beam #'positions = #(cons $beg $end) +#}) + +\relative { + \manualBeam #3 #6 c8 d e f +} +@end lilypond + @node Mathematics in functions @subsection Mathematics in functions -UNTRANSLATED NODE: IGNORE ME +Las funciones musicales pueden contar con programación de Scheme +además de la simple sustitución: + +@lilypond[quote,verbatim,ragged-right] +AltOn = #(define-music-function (parser location mag) (number?) + #{ \override Stem #'length = #$(* 7.0 mag) + \override NoteHead #'font-size = + #$(inexact->exact (* (/ 6.0 (log 2.0)) (log mag))) #}) + +AltOff = { + \revert Stem #'length + \revert NoteHead #'font-size +} + +{ c'2 \AltOn #0.5 c'4 c' + \AltOn #1.5 c' c' \AltOff c'2 } +@end lilypond + +@noindent +Este ejemplo se puede reescribir de forma que pase expresiones +musicales: + +@lilypond[quote,verbatim,ragged-right] +withAlt = #(define-music-function (parser location mag music) (number? ly:music?) + #{ \override Stem #'length = #$(* 7.0 mag) + \override NoteHead #'font-size = + #$(inexact->exact (* (/ 6.0 (log 2.0)) (log mag))) + $music + \revert Stem #'length + \revert NoteHead #'font-size #}) + +{ c'2 \withAlt #0.5 {c'4 c'} + \withAlt #1.5 {c' c'} c'2 } +@end lilypond + @node Void functions @subsection Void functions -UNTRANSLATED NODE: IGNORE ME +Una función musical debe devolver una expresión musical, per a veces +podemos necesitar una función en la que no hay música en juego (como +la desactivación de la funcionalidad Apuntar y Pulsar). Para hacerlo, +devolvemos una expresión musical @code{void} (vacía). + +Este es el motivo por el que la forma que se devuelve es +@code{(make-music ...)}. Con el valor de la propiedad @code{'void} +establecido a @code{#t}, le decimos al analizador que descarte la +expresión musical devuelta. así, la parte importante de la función +musical vacía es el proceso realizado por la función, no la expresión +musical que se devuelve. + +@example +noPointAndClick = +#(define-music-function (parser location) () + (ly:set-option 'point-and-click #f) + (make-music 'SequentialMusic 'void #t)) +... +\noPointAndClick % desactivar la funcionalidad Apuntar y Pulsar. +@end example + @node Functions without arguments @subsection Functions without arguments -UNTRANSLATED NODE: IGNORE ME +En casi todos los casos, una función sin argumentos se debe escribir +con una variable: + +@example +dolce = \markup@{ \italic \bold dolce @} +@end example + +Sin embargo, en raras ocasiones puede ser de utilidad crear una +función musical sin argumentos: + +@example +displayBarNum = +#(define-music-function (parser location) () + (if (eq? #t (ly:get-option 'display-bar-numbers)) + #@{ \once \override Score.BarNumber #'break-visibility = ##f #@} + #@{#@})) +@end example + +Para la imresión real de los números de compás donde se llama a esta +función, invoque a @command{lilypond} con + +@example +lilypond -d display-bar-numbers ARCHIVO.ly +@end example + @node Overview of available music functions @subsection Overview of available music functions -UNTRANSLATED NODE: IGNORE ME +@c fixme ; this should be move somewhere else? +Las siguientes instrucciones son funciones musicales: @include identifiers.tely + + @node Programmer interfaces @section Programmer interfaces -UNTRANSLATED NODE: IGNORE ME +Esta sección contiene información sobre cómo mezclar LilyPond y +Scheme. -@menu +@menu * Input variables and Scheme:: * Internal music representation:: -@end menu +@end menu + @node Input variables and Scheme @subsection Input variables and Scheme -UNTRANSLATED NODE: IGNORE ME +El formato de entrada contempla la noción de variables: en el ejemplo +siguiente, se asigna una expresión musical a una variable con el +nombre @code{traLaLa}. + +@example +traLaLa = @{ c'4 d'4 @} +@end example + +@noindent + +También existe una forma de ámbito léxico: en el ejemplo siguiente, el +bloque @code{\layout} también contiene una variable @code{traLaLa}, +que es independiente de la @code{\traLaLa} exterior. + +@example +traLaLa = @{ c'4 d'4 @} +\layout @{ traLaLa = 1.0 @} +@end example +@c +De hecho, cada archivo de entrada es un ámbito léxico, y todos los +bloques @code{\header}, @code{\midi} y @code{\layout} son ámbitos +anidados dentro de dicho ámbito de nivel superior. + +Tanto el ámbito léxico como las variables están implementados en el +sistema de módulos GUILE. Se adjunta un módulo anónimo de Scheme a +cada ámbito. Una asignación de la forma +@example +traLaLa = @{ c'4 d'4 @} +@end example + +@noindent +se convierte internamente a una definición de Scheme +@example +(define traLaLa @var{Scheme value of `@code{... }'}) +@end example + +Esto supone que las variables de entrada y las variables de Scheme se +pueden intermezclar con libertad. En el ejemplo siguiente, se +almacena un fragmento musical en la variable @code{traLaLa}, y se +dupplica utilizando Scheme. El resultado se importa en un bloque +@code{\score} por medio de una segunda variable @code{twice}: + +@lilypond[verbatim] +traLaLa = { c'4 d'4 } + +%% dummy action to deal with parser lookahead +#(display "this needs to be here, sorry!") + +#(define newLa (map ly:music-deep-copy + (list traLaLa traLaLa))) +#(define twice + (make-sequential-music newLa)) + +{ \twice } +@end lilypond + +@c Due to parser lookahead + +En este ejemplo, la asignación se produce después de que el analizador +sintáctico ha verificado que no ocurre nada interesante después de +@code{traLaLa = @{ ... @}}. Sin el argumento mudo del ejemplo, la +definición @code{newLa} se ejecuta antes de que se defina +@code{traLaLa}, conduciendo a un error de sintaxis. + +El ejemplo anterior muestra cómo @q{exportar} expresiones musicales +desde la entrada hasta el intérprete de Scheme. También es posible lo +contrario. Envolviendo un valor de Scheme en la función +@code{ly:export}, un valor de Scheme se interpreta como si hubiera +sido introducido en sintaxis de LilyPond. En vez de definir +@code{\twice}, el ejemplo anterior podría también haberse escrito como + +@example +... +@{ #(ly:export (make-sequential-music (list newLa))) @} +@end example + +El sódigo de Scheme se evalúa tan pronto como el analizador sintáctico +lo encuentra. Para definir código de Scheme en un macro (para +llamarlo con posterioridad), use @ref{Void functions}, o bien + +@example +#(define (nopc) + (ly:set-option 'point-and-click #f)) + +... +#(nopc) +@{ c'4 @} +@end example + +@knownissues + +No es posible mezclar variables de Scheme y de LilyPond con la opción +@code{--safe}. + @node Internal music representation @subsection Internal music representation -UNTRANSLATED NODE: IGNORE ME +Cuando se analiza sintácticamente una expresión musical, se convierte +en un conjunto de objetos musicales de Scheme. La propiedad que +define a un objeto musical es que tiene una cierta duración. El +tiempo es un número racional que mide la longitud de un fragmento de +música en unidades del valor de una redonda. + +Un objeto musical tiene tres clases de tipos: +@itemize +@item +nombre musical: cada expresión musical tiene un nombre. Por ejemplo, +una nota conduce a un evento @rinternals{NoteEvent}, y +@code{\simultaneous} conduce a @rinternals{SimultaneousMusic}. Hay +una lista de todas las expresiones que están disponibles en el Manual +de referencia de funcionamiento interno, bajo @rinternals{Music +expressions}. + +@item +@q{typo} o intterface: cada nombre de música tiene varios @q{tipos} o +interfaces, por ejemplo una nota es un @code{event}, pero también es +un @code{note-event}, un @code{rhythmic-event} y un +@code{melodic-event}. Todas las clases musicales se encuentran +relacionadas en la Referencia de funcionamiento interno bajo +@rinternals{Music classes}. + +@item +Objeto de C++: cada objeto musical está representado por un objeto de +la clase de C++ @code{Music}. +@end itemize + +La información real de una expresión musical se almacena en forma de +propiedades. Por ejemplo, un evento @rinternals{NoteEvent} tiene +propiedades @code{pitch} y @code{duration} que almacenan la altura y +duración de la nota. Hay una lista completa de las propiedades que +están disponibles en la Referencia de funcionamiento interno, bajo +@rinternals{Music properties}. + +Una expresión musical compuesta es un objeto musical que contiene +otros objetos musicales en sus propiedades. Se puede almacernar una +lista de objetos en la propiedad @code{elements} de un objeto musical, +o un solo objeto musical @q{hijo} en la propiedad @code{element}. Por +ejemplo, @rinternals{SequentialMusic} tiene sus hijos en +@code{elements}, y @rinternals{GraceMusic} tiene su elemento único en +@code{element}. El cuerpo de una repetición se almacena en la +propiedad @code{element} de @rinternals{RepeatedMusic}, y las +alternativas en @code{elements}. + @node Building complicated functions @section Building complicated functions -UNTRANSLATED NODE: IGNORE ME +Esta sección explica cómo reunir la información necesaria para crear +funciones musicales complejas. -@menu + +@menu * Displaying music expressions:: * Music properties:: * Doubling a note with slurs (example):: * Adding articulation to notes (example):: -@end menu +@end menu + @node Displaying music expressions @subsection Displaying music expressions -UNTRANSLATED NODE: IGNORE ME +@cindex interno, almacenamiento +@funindex \displayMusic + +Si se está escribiendo una función musical puede ser muy instructivo +examinar cómo se almacena internamente una expresión musical. Esto se +puede hacer con la función musical @code{\displayMusic}: + +@example +@{ + \displayMusic @{ c'4\f @} +@} +@end example + +@noindent +imprime lo siguiente: + +@example +(make-music + 'SequentialMusic + 'elements + (list (make-music + 'EventChord + 'elements + (list (make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 0 0)) + (make-music + 'AbsoluteDynamicEvent + 'text + "f"))))) +@end example + +De forma predeterminada, LilyPond imprime estos mensajes en la consola +junto al resto de los mensajes. Para discernir entre estos mensajes y +guardar el resultado de @code{\display@{MATERIAL@}}, redirija la +salida hacia un archivo. + +@example +lilypond archivo.ly >resultado.txt +@end example + +Con la aplicación de un poco de formato, la inforamción anterior es +fácil de leer: + +@example +(make-music 'SequentialMusic + 'elements (list (make-music 'EventChord + 'elements (list (make-music 'NoteEvent + 'duration (ly:make-duration 2 0 1 1) + 'pitch (ly:make-pitch 0 0 0)) + (make-music 'AbsoluteDynamicEvent + 'text "f"))))) +@end example + +Una secuencia musical @code{@{ ... @}} tiene el nombre +@code{SequentialMusic}, y sus expresiones internas se almacenan como +una lista en su propiedad @code{'elements}. Una nota se representa +como una expresión @code{EventChord} que contiene un objeto +@code{NoteEvent} (que almacena las propiedades de duración y altura) y +cualquier otra información adicional (en este caso, un evento +@code{AbsoluteDynamicEvent} con una porpiedad de texto @code{"f"}. + @node Music properties @subsection Music properties -UNTRANSLATED NODE: IGNORE ME +El objeto @code{NoteEvent} es el primer objeto de la propiedad +@code{'elements} de @code{someNote}. + +@example +unaNota = c' +\displayMusic \unaNota +===> +(make-music + 'EventChord + 'elements + (list (make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 0 0)))) +@end example + +La función @code{display-scheme-music} es la función utilizada por +@code{\displayMusic} para imprimir la representación de Scheme de una +expresión musical. + +@example +#(display-scheme-music (first (ly:music-property unaNota 'elements))) +===> +(make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 0 0)) +@end example + +Después se accede a la altura de la nota a través de la propiedad +@code{'pitch} del objeto @code{NoteEvent}: + +@example +#(display-scheme-music + (ly:music-property (first (ly:music-property unaNota 'elements)) + 'pitch)) +===> +(ly:make-pitch 0 0 0) +@end example + +La altura de la nota se puede cambiar estableciendo el valor de esta +propiedad 'pitch: + +@funindex \displayLilyMusic + +@example +#(set! (ly:music-property (first (ly:music-property unaNota 'elements)) + 'pitch) + (ly:make-pitch 0 1 0)) ;; fijar la altura a d'. +\displayLilyMusic \unaNota +===> +d' +@end example + @node Doubling a note with slurs (example) @subsection Doubling a note with slurs (example) -UNTRANSLATED NODE: IGNORE ME +Supongamos que queremos crear una función que traduce una entrada como +@code{a} a algo como @code{a( a)}. Empezamos examinando la +representación interna de la música con la que queremos terminar. + +@example +\displayMusic@{ a'( a') @} +===> +(make-music + 'SequentialMusic + 'elements + (list (make-music + 'EventChord + 'elements + (list (make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 5 0)) + (make-music + 'SlurEvent + 'span-direction + -1))) + (make-music + 'EventChord + 'elements + (list (make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 5 0)) + (make-music + 'SlurEvent + 'span-direction + 1))))) +@end example + +Las malas noticias son que las espresiones @code{SlurEvent} se deben +añadir @q{dentro} de la nota (o más concretamente, dentro de la +expresión @code{EventChord}). + +Ahora observamos la entrada: + +@example +(make-music + 'SequentialMusic + 'elements + (list (make-music + 'EventChord + 'elements + (list (make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 5 0)))))) +@end example + +Así pues, en nuestra función, tenemos que clonar esta expresión (de +forma que tengamos dos notas para construir la secuencia), añadir +@code{SlurEvents} a la propiedad @code{'elements} de cada una de +ellas, y por último hacer una secuencia @code{SequentialMusic} con los +dos @code{EventChords}. + +@example +doubleSlur = #(define-music-function (parser location note) (ly:music?) + "Return: @{ note ( note ) @}. + `note' is supposed to be an EventChord." + (let ((note2 (ly:music-deep-copy note))) + (set! (ly:music-property note 'elements) + (cons (make-music 'SlurEvent 'span-direction -1) + (ly:music-property note 'elements))) + (set! (ly:music-property note2 'elements) + (cons (make-music 'SlurEvent 'span-direction 1) + (ly:music-property note2 'elements))) + (make-music 'SequentialMusic 'elements (list note note2)))) +@end example + @node Adding articulation to notes (example) @subsection Adding articulation to notes (example) -UNTRANSLATED NODE: IGNORE ME +La manera fácil de añadir articulación a las notas es fundir dos +expresiones musicales en un contexto único, como está explicado en +@ref{Creating contexts}. Sin embargo, suponga que queremos escribir +una función musical que haga esto. + +Una @code{$variable} dentro de la notación @code{#@{...#@}} es como +usar una @code{\variable} normal en la notación clásica de LilyPond. +Sabemos que + +@example +@{ \musica -. -> @} +@end example + +@noindent +no funciona en LilyPond. Podemos evitar este problema adjuntando la +articulación a una nota de mentira, + +@example +@{ << \musica s1*0-.-> @} +@end example + +@noindent +pero a los efectos de este ejemplo, aprenderemos ahora cómo hacerlo en +Scheme. Comenzamos examinando nuestra entrada y la salida deseada: + +@example +% entrada +\displayMusic c4 +===> +(make-music + 'EventChord + 'elements + (list (make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch -1 0 0)))) +===== +% salida deseada +\displayMusic c4-> +===> +(make-music + 'EventChord + 'elements + (list (make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch -1 0 0)) + (make-music + 'ArticulationEvent + 'articulation-type + "marcato"))) +@end example + +Vemos que una nota (@code{c4}) se representa como una expresión +@code{EventChord}, con una expresión @code{NoteEvent} en su lista de +elementos. Para añadir una articulación marcato, se debe añadir una +expresión @code{ArticulationEvent} a la propiedad elementos de la +expresión @code{EventChord}. + +Para construir esta función, empezamos con + +@example +(define (add-marcato event-chord) + "Añadir una ArticulationEvent de marcato a los elementos de `event-chord', + que se ssupone que es una expresión EventChord." + (let ((result-event-chord (ly:music-deep-copy event-chord))) + (set! (ly:music-property result-event-chord 'elements) + (cons (make-music 'ArticulationEvent + 'articulation-type "marcato") + (ly:music-property result-event-chord 'elements))) + result-event-chord)) +@end example + +La primera línea es la forma de definir una función en Scheme: el +nombre de la función es @code{add-marcato}, y tiene una variable +llamada @code{event-chord}. En Scheme, el tipo de variable suele +quedar claro a partir de su nombre (¡esto también es una buena +práctica en otros lenguajes de programación!). + +@example +"Añadir una ArticulationEvent de marcato..." +@end example + +@noindent +es una descripción de lo que hace la función. No es estrictamente +necesario, pero como los nombres de variable claros, es una buena +práctica. + +@example +(let ((result-event-chord (ly:music-deep-copy event-chord))) +@end example + +@code{let} se usa para declarar variables locales. Aquí usamos una +variable local, llamada @code{result-event-chord}, a la que le damos +el valor @code{(ly:music-deep-copy event-chord)}. +@code{ly:music-deep-copy} es una función específica de LilyPond, como +todas las funciones que comienzan por @code{ly:}. Se usa para hacer +una copia de una expresión musical. Aquí, copiamos @code{event-chord} +(el parámetro de la función). Recuerde que el propósito es añadir un +marcato a una expresión @code{EventChord}. Es mejor no modificar el +@code{EventChord} que se dio como argumento, porque podría utilizarse +en algún otro lugar. + +Ahora tenemos un @code{result-event-chord}, que es una expresión +@code{NoteEventChord} y es una copia de @code{event-chord}. Añadimos +el marcato a su propiedad lista de elementos. + +@example +(set! place new-value) +@end example + +Aquí, lo que queremos establecer (el @q{place}) es la propiedad +@q{elements} de la expresión @code{result-event-chord}. + +@example +(ly:music-property result-event-chord 'elements) +@end example + +@code{ly:music-property} es la función que se usa para acceder a las +propiedades musicales (los @code{'elements}, @code{'duration}, +@code{'pitch}, etc., que vemos en la salida de @code{\displayMusic} +más arriba). El nuevo valor es la anterior propiedad elements, con un +elemento adicional: la expresión @code{ArticulationEvent}, que +copiamos a partir de la salida de @code{\displayMusic}, + +@example +(cons (make-music 'ArticulationEvent + 'articulation-type "marcato") + (ly:music-property result-event-chord 'elements)) +@end example + +@code{cons} se usa para añadir un elemento a una lsita sin modificar +la lista original. Esto es lo que queremos: la misma lista que antes, +más la nueva expresión @code{ArticulationEvent}. El orden dentro de +la propiedad elements no es importante aquí. + +Finalmente, una vez añadida la articulación marcato a su propiedad +@code{elements}, podemos devolver @code{result-event-chord}, de aquí +la última línea de la función. + +Ahora transformamos la función @code{add-marcato} en una función +musical, + +@example +addMarcato = #(define-music-function (parser location event-chord) + (ly:music?) + "Add a marcato ArticulationEvent to the elements of `event-chord', + which is supposed to be an EventChord expression." + (let ((result-event-chord (ly:music-deep-copy event-chord))) + (set! (ly:music-property result-event-chord 'elements) + (cons (make-music 'ArticulationEvent + 'articulation-type "marcato") + (ly:music-property result-event-chord 'elements))) + result-event-chord)) +@end example + +Podemos verificar que esta función musical funciona correctamente, + +@example +\displayMusic \addMarcato c4 +@end example + @node Markup programmer interface @section Markup programmer interface -UNTRANSLATED NODE: IGNORE ME +Los marcados están implementados como funciones de Scheme especiales +que producen un elemento Stencil (sello) dado un número de argumentos. -@menu +@menu * Markup construction in Scheme:: * How markups work internally:: * New markup command definition:: * New markup list command definition:: -@end menu +@end menu + @node Markup construction in Scheme @subsection Markup construction in Scheme -UNTRANSLATED NODE: IGNORE ME +@cindex marcado, definir instrucciones de + +El macro @code{markup} construye expresiones de marcado en Scheme, +proporcionando una sintaxis similar a la de LilyPond. Por ejemplo: + +@example +(markup #:column (#:line (#:bold #:italic "hola" #:raise 0.4 "mundo") + #:larger #:line ("fulano" "fulanito" "menganito"))) +@end example + +@noindent +equivale a: +@example +\markup \column @{ \line @{ \bold \italic "hola" \raise #0.4 "mundo" @} + \larger \line @{ fulano fulanito menganito @} @} +@end example + +@noindent +Este ejemplo muestra las principales reglas de traducción entre la +sitaxis del marcado normal de LilyPond y la sintaxis del marcado de +Scheme. + +@quotation +@multitable @columnfractions .3 .3 +@item @b{LilyPond} @tab @b{Scheme} +@item @code{\markup marcado1} @tab @code{(markup marcado1)} +@item @code{\markup @{ marcado1 marcado2 ... @}} @tab + @code{(markup marcado1 marcado2 ... )} +@item @code{\instruccion} @tab @code{#:instruccion} +@item @code{\variable} @tab @code{variable} +@item @code{\center-column @{ ... @}} @tab @code{#:center-column ( ... )} +@item @code{cadena} @tab @code{"cadena"} +@item @code{#argumento-de-scheme} @tab @code{argumento-de-scheme} +@end multitable +@end quotation + +Todo el lenguaje Scheme está accesible dentro del macro @code{markup}. +Por ejemplo, podemos usar llamadas a funciones dentro de @code{markup} +para así manipular cadenas de caracteres. Esto es útil si se están +definiendo instrucciones de marcado nuevas (véase @ref{New markup +command definition}). + +@knownissues + +El argumento markup-list de instrucciones como @code{#:line}, +@code{#:center} y @code{#:column} no pueden se una variable o el +resultado de la llamada a una función. + +@lisp +(markup #:line (funcion-que-devuelve-marcados)) +@end lisp + +@noindent +no es válido. Hay que usar las funciones @code{make-line-markup}, +@code{make-center-markup} o @code{make-column-markup} en su lugar: + +@lisp +(markup (make-line-markup (funcion-que-devuelve-marcados))) +@end lisp + @node How markups work internally @subsection How markups work internally -UNTRANSLATED NODE: IGNORE ME +En un elemento de marcado como + +@example +\raise #0.5 "ejemplo de texto" +@end example + +@noindent +@code{\raise} se representa en realidad por medio de la función +@code{raise-markup}. La expresión de marcado se almacena como + +@example +(list raise-markup 0.5 (list simple-markup "ejemplo de texto")) +@end example + +Cuando el marcado se convierte en objetos imprimibles (Stencils o +sellos), se llama la función @code{raise-markup} como + +@example +(apply raise-markup + @var{\objeto de marcado} + @var{lista de listas asociativas de propiedades} + 0.5 + @var{el marcado "ejemplo de texto"}) +@end example + +Primero la función @code{raise-markup} crea el sello para la cadena +@code{ejemplo de texto}, y después eleva el sello Stencil en 0.5 +espacios de pentagrama. Este es un ejemplo bastante simple; en el +resto de la sección podrán verse ejemplos más complejos, así como en +@file{scm/@/define@/-markup@/-commands@/.scm}. + @node New markup command definition @subsection New markup command definition -UNTRANSLATED NODE: IGNORE ME +Las instrucciones de marcado nuevas se pueden definir con el macro de +Scheme @code{define-markup-command}. + +@lisp +(define-markup-command (@var{nombre-de-la-instruccion} @var{layout} @var{props} @var{arg1} @var{arg2} ...) + (@var{arg1-type?} @var{arg2-type?} ...) + ..command body..) +@end lisp + +Los argumentos son + +@table @var +@item argi +@var{i}-ésimo argumento de la instrucción +@item argi-type? +predicado de tipo para el argumento @var{i}-ésimo +@item layout +la definición de @q{presentación} +@item props +lista de listas asociativas, que contiene todas las propiedades +activas. +@end table + +Como ejemplo sencillo, mostramos cómo añadir una instrucción +@code{\smallcaps}, que selecciona una tipografía de versalitas. +Normalmente podríamos seleccionar la tipografía de versalitas, + +@example +\markup @{ \override #'(font-shape . caps) Texto-en-versalitas @} +@end example + +@noindent +Esto selecciona la tipografía de versalitas mediante el +establecimiento de la propiedad @code{font-shape} a @code{#'caps} para +la interpretación de @code{Texto-en-versalitas}. + +Para poner lo anterior disponible como la instrucción +@code{\smallcaps}, tenemos que definir una función utilizando +@code{define-markup-command}. La instrucción ha de tomar un argumento +del tipo @code{markup}. Por tanto, el inicio de la definición ha de +ser + +@example +(define-markup-command (smallcaps layout props argument) (markup?) +@end example + +@noindent + +Lo que aparece a continuación es el contenido de la instrucción: +debemos interpretar el @code{argument} como un marcado, es decir: + +@example +(interpret-markup layout @dots{} argument) +@end example + +@noindent +Esta interpretación tiene que añadir @code{'(font-shape . caps)} a las +propiedades activas, por lo que sustituimos lo siguiente por los +@dots{} en el ejemplo anterior: + +@example +(cons (list '(font-shape . caps) ) props) +@end example + +@noindent +La variable @code{props} es una lista de a-listas, y se lo anteponemos +haciendo la operación cons de una lista con el ajuste adicional. + +Supongamos que estamos tipografiando un recitativo de una ópera y nos +gustaría definir una instrucción que presente los nombres de los +personajes de una forma personalizada. Queremos que los nombres se +impriman con versalitas y se desplacen un poco a la izquierda y hacia +arriba. Definimos una instrucción @code{\character} que toma en +cuenta la traslación necesaria y utiliza la instrucción +@code{\smallcaps} recién definida: + +@example +#(define-markup-command (character layout props nombre) (string?) + "Imprimir el nombre del personaje en versalitas, desplazado a la izquierda y hacia + arriba. Sintaxis: \\character #\"nombre\"" + (interpret-markup layout props + (markup #:hspace 0 #:translate (cons -3 1) #:smallcaps nombre))) +@end example + +Esta es una complicación que requiere una explicación: los textos por +encima y por debajo del pentagrama se mueven verticalmente de forma +que estén a una cierta distancia (la propiedad @code{padding}) del +pentagrama y de las notas. Para asegurar que este mecanismo no anula +el efecto de nuestro @code{#:translate}, añadimos una cadena vacía +(@code{#:hspace 0}) antes del texto trasladado. Ahora el +@code{#:hspace 0} se pone encima de las notas, y el @code{nombre} se +mueve en relación a dicha cadena vacía. El efecto neto es que el +texto se mueve hacia la izquierda y hacia arriba. + +El resultado final es como sigue: + +@example +@{ + c''^\markup \character #"Cleopatra" + e'^\markup \character #"Giulio Cesare" +@} +@end example + +@lilypond[quote,ragged-right] +#(define-markup-command (smallcaps layout props str) (string?) + "Print the string argument in small caps. Syntax: \\smallcaps #\"string\"" + (interpret-markup layout props + (make-line-markup + (map (lambda (s) + (if (= (string-length s) 0) + s + (markup #:large (string-upcase (substring s 0 1)) + #:translate (cons -0.6 0) + #:tiny (string-upcase (substring s 1))))) + (string-split str #\Space))))) + +#(define-markup-command (character layout props name) (string?) + "Print the character name in small caps, translated to the left and + top. Syntax: \\character #\"name\"" + (interpret-markup layout props + (markup #:hspace 0 #:translate (cons -3 1) #:smallcaps name))) + +{ + c''^\markup \character #"Cleopatra" c'' c'' c'' + e'^\markup \character #"Giulio Cesare" e' e' e' +} +@end lilypond + +Hemos usado la forma de fuente tipográfica @code{caps}, pero +supongamos que nuestra fuente no tiene la variante de versalitas. En +ese caso tenemos que hacer una falsa fuente de mayúsculas pequeñas +haciendo que la cadena en mayúsculas tenga la primera legra un poco +mayor: + +@example +#(define-markup-command (smallcaps layout props str) (string?) + "Print the string argument in small caps." + (interpret-markup layout props + (make-line-markup + (map (lambda (s) + (if (= (string-length s) 0) + s + (markup #:large (string-upcase (substring s 0 1)) + #:translate (cons -0.6 0) + #:tiny (string-upcase (substring s 1))))) + (string-split str #\Space))))) +@end example + +La instrucción @code{smallcaps} primero divide su argumento de cadena +en unidades o palabras separadas por espacios (@code{(string-split str +#\Space)}); para cada unidad o palabra, se construye un marcado con la +primera letra agrandada y en mayúscula (@code{#:large (string-upcase +(substring s 0 1))}), y un segundo marcado construido con las letras +siguientes reducidas de tamaño y en mayúsculas (@code{#:tiny +(string-upcase (substring s 1))}). Como LilyPond introduce un espacio +entre los marcados de una misma línea, el segundo marcado se traslada +a la izquierda (@code{#:translate (cons -0.6 0) ...}). Después, los +marcados construidos para cada palabra se ponen en una línea mediante +@code{(make-line-markup ...)}. Finalmente, el marcado resultante se +pasa a la función @code{interpret-markup}, con los argumentos +@code{layout} y @code{props}. + +Nota: ahora existe una instrucción interna @code{\smallCaps} que se +puede usar para poner texto en versalitas. Consulte @ref{Text markup +commands}, para ver más detalles. + +@knownissues + +Actualmente las combinaciones de argumentos que hay disponibles +(después de los argumentos estándar @var{layout} y @var{props}) para +una instrucción de marcado definida con @code{define-markup-command} +se limitan a la siguiente lista: + +@table @asis +@item (ningún argumento) +@itemx @var{list} +@itemx @var{markup} +@itemx @var{markup markup} +@itemx @var{scm} +@itemx @var{scm markup} +@itemx @var{scm scm} +@itemx @var{scm scm markup} +@itemx @var{scm scm markup markup} +@itemx @var{scm markup markup} +@itemx @var{scm scm scm} +@end table + +@noindent +En la tabla de arriba, @var{scm} representa los tipos de datos nativos +de Scheme como @q{number} (número) o @q{string} (cadena). + +Como ejemplo, no es posible usar una instrucción de marcado +@code{fulanito} con cuatro argumentos definida como + +@example +#(define-markup-command (fulanito layout props + num1 str1 num2 str2) + (number? string? number? string?) + ...) +@end example + +@noindent +Si la aplicamos como, digamos, + +@example +\markup \fulanito #1 #"mengano" #2 #"zutano" +@end example + +@cindex Scheme signature +@cindex signature, Scheme +@noindent +@command{lilypond} protesta diciendo que no puede analizar +@code{fulanito} debido a su firma de Scheme desconocida. + @node New markup list command definition @subsection New markup list command definition -UNTRANSLATED NODE: IGNORE ME +Las instrucciones de listas de marcado se definen con el macro de +Scheme @code{define-markup-list-command}, que es similar al macro +@code{define-markup-command} descrito en @ref{New markup command +definition}, excepto que donde éste devuelve un sello único, aquél +devuelve una lista de sellos. + +En el siguiente ejemplo se define una instrucción de lista de marcado +@code{\paragraph}, que devuelve una lista de líneas justificadas, +estando la primera de ellas sangrada. La anchura del sangrado se toma +del argumento @code{props}. + +@example +#(define-markup-list-command (paragraph layout props args) (markup-list?) + (let ((indent (chain-assoc-get 'par-indent props 2))) + (interpret-markup-list layout props + (make-justified-lines-markup-list (cons (make-hspace-markup indent) + args))))) +@end example + +Aparte de los argumentos usuales @code{layout} y @code{props}, la +instrucción de lista de marcados @code{paragraph} toma un argumento de +lista de marcados, llamado @code{args}. El predicado para listas de +marcados es @code{markup-list?}. + +Em primer lugar, la función toma el ancho del sangrado, una propiedad +llamada aquí @code{par-indent}, de la lista de propiedades +@code{props}. Si no se encuentra la propiedad, el valor +predeterminado es @code{2}. Después, se hace una lista de líneas +justificadas usando la función +@code{make-justified-lines-markup-list}, que está relacionada con la +instrucción incorporada de lista de marcados @code{\justified-lines}. +Se añade un espacio horizontal al principio usando la función +@code{make-hspace-markup}. Finalmente, la lista de marcados se +interpreta usando la función @code{interpret-markup-list}. + +Esta nueva instrucción de lista de marcados se puede usar como sigue: + +@example +\markuplines @{ + \paragraph @{ + El arte de la tipografía musical se llama \italic @{grabado (en plancha).@} + El término deriva del proceso tradicional de impresión de música. + hace sólo algunas décadas, las partituras se hacían cortando y estampando + la música en una plancha de zinc o lata en una imagen invertida. + @} + \override-lines #'(par-indent . 4) \paragraph @{ + La plancha se tenía que entintar, y las depresiones causadas por los cortes + y estampados retienen la tinta. Se formaba una imagen presionando el papel + contra la plancha. El estampado y cortado se hacía completamente + a mano. + @} +@} +@end example + @node Contexts for programmers @section Contexts for programmers -UNTRANSLATED NODE: IGNORE ME - -@menu +@menu * Context evaluation:: * Running a function on all layout objects:: -@end menu +@end menu + @node Context evaluation @subsection Context evaluation -UNTRANSLATED NODE: IGNORE ME +@cindex código, llamadas durante la interpretación +@funindex \applyContext + +Se pueden modificar los contextos durante la interpretación con código +de Scheme. La sintaxis para esto es + +@example +\applyContext @var{función} +@end example + +@var{función} debe ser una función de Scheme que toma un único +argumento, que es el contexto al que aplicarla. El código siguiente +imprime el número del compás actual sobre la salida estándar durante +la compilación: + +@example +\applyContext + #(lambda (x) + (format #t "\nSe nos ha llamado en el compás número ~a.\n" + (ly:context-property x 'currentBarNumber))) +@end example + @node Running a function on all layout objects @subsection Running a function on all layout objects -UNTRANSLATED NODE: IGNORE ME + +@cindex código, llamar sobre objetos de presentación +@funindex \applyOutput + + +La manera más versátil de realizar el ajuste fino de un objeto es +@code{\applyOutput}. Su sintaxis es + +@example +\applyOutput @var{contexto} @var{proc} +@end example + +@noindent +donde @var{proc} es una función de Scheme, que toma tres argumentos. + +Al interpretarse, la función @var{proc} se llama para cada objeto de +presentación que se encuentra en el contexto @var{contexto}, con los +siguientes argumentos: + +@itemize +@item el propio objeto de presentación, +@item el contexto en que se creó el objeto de presentación, y +@item el contexto en que se procesa @code{\applyOutput}. +@end itemize + +Además, la causa del objeto de presentación, es decir el objeto o +expresión musical que es responsable de haberlo creado, está en la +propiedad @code{cause} del objeto. Por ejemplo, para la cabeza de una +nota, éste es un evento @rinternals{NoteHead}, y para un objeto +@rinternals{Stem} (plica), éste es un objeto @rinternals{Stem}. +@c Impossible - changed to Stem --FV + +He aquí una función que usar para @code{\applyOutput}; borra las +cabezas de las notas que están sobre la línea central: + +@example +(define (blanker grob grob-origin context) + (if (and (memq (ly:grob-property grob 'interfaces) + note-head-interface) + (eq? (ly:grob-property grob 'staff-position) 0)) + (set! (ly:grob-property grob 'transparent) #t))) +@end example + @node Scheme procedures as properties @section Scheme procedures as properties -UNTRANSLATED NODE: IGNORE ME +Las propiedades (como el grosor, la dirección, etc.) se pueden +establecer a valores fijos con \override, p. ej. + +@example +\override Stem #'thickness = #2.0 +@end example + +Las propiedades pueden fijarse también a un procedimiento de scheme, + +@lilypond[fragment,verbatim,quote,relative=2] +\override Stem #'thickness = #(lambda (grob) + (if (= UP (ly:grob-property grob 'direction)) + 2.0 + 7.0)) +c b a g b a g b +@end lilypond + +@noindent +En este caso, el procedimiento se ejecuta tan pronto como el valor de +la propiedad se reclama durante el proceso de formateo. + +Casi todo el motor de tipografiado está manejado por estos +@emph{callbacks}. Entre las propiedades que usan normalmente +@emph{callbacks} están + +@table @code +@item stencil + La rutina de impresión, que construye un dibujo para el símbolo +@item X-offset + La rutina que establece la posición horizontal +@item X-extent + La rutina que calcula la anchura de un objeto +@end table + +El procedimiento siempre toma un argumento único, que es el grob (el +objeto gráfico). + +Si se deben llamar rutinas con varios argumentos, el grob actual se +puede insertar con una cerradura de grob. He aquí un ajuste +procedente de @code{AccidentalSuggestion}, + +@example +(X-offset . + ,(ly:make-simple-closure + `(,+ + ,(ly:make-simple-closure + (list ly:self-alignment-interface::centered-on-x-parent)) + ,(ly:make-simple-closure + (list ly:self-alignment-interface::x-aligned-on-self))))) +@end example + +@noindent +En este ejemplo, tanto +@code{ly:self-alignment-interface::x-aligned-on-self} como +@code{ly:self-alignment-interface::centered-on-x-parent} se llaman con +el grob como argumento. El resultado se añade con la función +@code{+}. Para asegurar que esta adición se ejecuta adecuadamente, +todo ello se encierra dentro de @code{ly:make-simple-closure}. + +De hecho, usar un solo procedimiento como valor de una propiedad +equivale a + +@example +(ly:make-simple-closure (ly:make-simple-closure (list @var{proc}))) +@end example + +@noindent +El @code{ly:make-simple-closure} interior aporta el grob como +argumento de @var{proc}, el exterior asegura que el resultado de la +función es lo que se devuelve, en lugar del objeto +@code{simple-closure}. + + +@node Using Scheme code instead of \tweak +@section Using Scheme code instead of @code{\tweak} + +La principal desventaja de @code{\tweak} es su inflexibilidad +sintáctica. Por ejemplo, lo siguiente produce un error de sintaxis. + +@example +F = \tweak #'font-size #-3 -\flageolet + +\relative c'' @{ + c4^\F c4_\F +@} +@end example + +@noindent +En otras palabras, @code{\tweak} no se comporta como una articulación +en cuando a la sintaxis; concretamente, no se puede adjuntar con +@code{^} y @code{_}. + +Usando Scheme, se puede dar un rodeo a este problema. La ruta hacia +el resultado se da en @ref{Adding articulation to notes (example)}, +especialmente cómo usar @code{\displayMusic} como guía de ayuda. + +@example +F = #(let ((m (make-music 'ArticulationEvent + 'articulation-type "flageolet"))) + (set! (ly:music-property m 'tweaks) + (acons 'font-size -3 + (ly:music-property m 'tweaks))) + m) + +\relative c'' @{ + c4^\F c4_\F +@} +@end example + +@noindent +Aquí, las propiedades @code{tweaks} del objeto flageolet @code{m} +(creado con @code{make-music}) se extraen con +@code{ly:music-property}, se antepone un nuevo par clave-valor para +cambiar el tamaño de la tipografía a la lista de propiedades con la +función de Scheme @code{acons}, y finalmente el resultado se escribe +de nuevo con @code{set!}. El último elemento del bloque @code{let} es +el valor de retorno, el propio @code{m}. + +@node Difficult tweaks +@section Difficult tweaks + +Hay un cierto número de tipos de ajustes difíciles. + +@itemize + +@item +Un tipo de ajuste difícil es la apariencia de los objetos de +extensión, como las ligaduras de expresión y de unión. Inicialmente, +sólo se crea uno de estos objetos, y pueden ajustarse con el mecanismo +normal. Sin embargo, en ciertos casos los objetos extensores cruzan +los saltos de línea. Si esto ocurre, estos objetos se clonan. Se +crea un objeto distinto por cada sistema en que se encuentra. Éstos +son clones del objeto original y heredan todas sus propiedades, +incluidos los @code{\override}s. + +En otras palabras, un @code{\override} siempre afecta a todas las +piezas de un objeto de extensión fragmentado. Para cambiar sólo una +parte de un extensor en el salto de línea, es necesario inmiscuirse en +el proceso de formateado. El @emph{callback} +@code{after-line-breaking} contiene el procedimiento Scheme que se +llama después de que se han determinado los saltos de línea, y los +objetos de presentación han sido divididos sobre los distintos +sistemas. + +En el ejemplo siguiente, definimos un procedimiento +@code{my-callback}. Este procedimiento + +@itemize +@item +determina si hemos sido divididos por los saltos de línea +@item +en caso afirmativo, reúne todos los objetos divididos +@item +comprueba si somos el último de los objetos divididos +@item +en caso afirmativo, establece @code{extra-offset}. +@end itemize + +Este procedimiento se instala en @rinternals{Tie} (ligadura de unión), +de forma que la última parte de la ligadura dividida se traslada hacia +arriba. + +@lilypond[quote,verbatim,ragged-right] +#(define (my-callback grob) + (let* ( + ; have we been split? + (orig (ly:grob-original grob)) + + ; if yes, get the split pieces (our siblings) + (siblings (if (ly:grob? orig) + (ly:spanner-broken-into orig) '() ))) + + (if (and (>= (length siblings) 2) + (eq? (car (last-pair siblings)) grob)) + (ly:grob-set-property! grob 'extra-offset '(-2 . 5))))) + +\relative c'' { + \override Tie #'after-line-breaking = + #my-callback + c1 ~ \break c2 ~ c +} +@end lilypond + +@noindent +Al aplicar este truco, la nueva función de callback +@code{after-line-breaking} también debe llamar a la antigua +@code{after-line-breaking}, si existe. Por ejemplo, si se usa con +@code{Hairpin}, se debe llamar también a +@code{ly:hairpin::after-line-breaking}. + + +@item Algunos objetos no se pueden cambiar con @code{\override} por +razones técnicas. Son ejemplos @code{NonMusicalPaperColumn} y +@code{PaperColumn}. Se pueden cambiar con la función +@code{\overrideProperty} que funciona de forma similar a @code{\once +\override}, pero usa una sintaxis distinta. + +@example +\overrideProperty +#"Score.NonMusicalPaperColumn" % Nombre del grob +#'line-break-system-details % Nombre de la propiedad +#'((next-padding . 20)) % Valor +@end example +Observe, sin embargo, que @code{\override}, aplicado a +@code{NoteMusicalPaperColumn} y a @code{PaperColumn}, aún funciona +como se espera dentro de los bloques @code{\context}. --- SKELETON FILE -- -When you actually translate this file, please remove these lines as -well as all `UNTRANSLATED NODE: IGNORE ME' lines. +@end itemize