]> git.donarmstrong.com Git - lilypond.git/blob - scm/new-markup.scm
* scm/define-grobs.scm (all-grob-descriptions): remove
[lilypond.git] / scm / new-markup.scm
1 "
2 Internally markup is stored as lists, whose head is a function.
3
4   (FUNCTION ARG1 ARG2 ... )
5
6 When the markup is formatted, then FUNCTION is called as follows
7
8   (FUNCTION GROB PROPS ARG1 ARG2 ... ) 
9
10 GROB is the current grob, PROPS is a list of alists, and ARG1.. are
11 the rest of the arguments.
12
13 The function should return a molecule (i.e. a formatted, ready to
14 print object).
15
16
17 To add a function, use the def-markup-command utility.
18
19   (def-markup-command (mycommand paper prop arg1 ...) (arg1-type? ...)
20     \"my command usage and description\"
21     ...function body...)
22
23 The command is now available in markup mode, e.g.
24
25
26   \\markup { .... \\MYCOMMAND #1 argument ... }
27
28 " ; "
29
30 ;;;;;;;;;;;;;;;;;;;;;;;;;;;
31 ;;; markup definer utilities
32 ;;; `def-markup-command' can be used both for built-in markup
33 ;;; definitions and user defined markups.
34
35 (defmacro-public def-markup-command (command-and-args signature . body)
36   "Define a COMMAND-markup function after command-and-args and body,
37 register COMMAND-markup and its signature,
38 add COMMAND-markup to markup-function-list,
39 sets COMMAND-markup markup-signature and markup-keyword object properties,
40 define a make-COMMAND-markup function.
41 Syntax:
42   (def-markup-command (COMMAND paper props arg1 arg2 ...) (arg1-type? arg2-type? ...)
43     \"documentation string\"
44     ...command body...)
45  or:
46   (def-markup-command COMMAND (arg1-type? arg2-type? ...)
47     function)
48 "
49   (let* ((command (if (pair? command-and-args) (car command-and-args) command-and-args))
50          (args (if (pair? command-and-args) (cdr command-and-args) '()))
51          (command-name (string->symbol (string-append (symbol->string command) "-markup")))
52          (make-markup-name (string->symbol (string-append "make-" (symbol->string command-name)))))
53     `(begin
54        (define-public ,(if (pair? args)
55                            (cons command-name args)
56                            command-name)
57          ,@body)
58        (set! (markup-command-signature ,command-name) (list ,@signature))
59        (if (not (member ,command-name markup-function-list))
60            (set! markup-function-list (cons ,command-name markup-function-list)))
61        (define-public (,make-markup-name . args)
62          (let ((sig (list ,@signature)))
63            (make-markup ,command-name ,(symbol->string make-markup-name) sig args))))))
64
65 (define-public (make-markup markup-function make-name signature args)
66   " Construct a markup object from MARKUP-FUNCTION and ARGS. Typecheck
67 against SIGNATURE, reporting MAKE-NAME as the user-invoked function.
68 "
69   (let* ((arglen (length args))
70          (siglen (length signature))
71          (error-msg (if (and (> siglen 0) (> arglen 0))
72                         (markup-argument-list-error signature args 1)
73                         #f)))
74     (if (or (not (= arglen siglen)) (< siglen 0) (< arglen 0))
75         (scm-error 'markup-format make-name
76                    "Expect ~A arguments for ~A. Found ~A: ~S"
77                    (list siglen make-name arglen args)
78                    #f))
79     (if error-msg
80         (scm-error 'markup-format make-name
81                    "Invalid argument in position ~A\nExpect: ~A\nFound: ~S."
82                    error-msg #f)
83         (cons markup-function args))))
84
85 ;;;;;;;;;;;;;;;
86 ;;; Utilities for storing and accessing markup commands signature
87 ;;; and keyword.
88 ;;; Examples:
89 ;;;
90 ;;; (set! (markup-command-signature raise-markup) (list number? markup?))
91 ;;; ==> ((#<primitive-procedure number?> #<procedure markup? (obj)>) . scheme0-markup1)
92 ;;;
93 ;;; (markup-command-signature raise-markup)
94 ;;; ==> (#<primitive-procedure number?> #<procedure markup? (obj)>)
95 ;;;
96 ;;; (markup-command-keyword raise-markup) ==> "scheme0-markup1"
97 ;;; 
98
99 (define markup-command-signatures (make-hash-table 50))
100
101 (define (markup-command-signature-ref markup-command)
102   "Return markup-command's signature, e.g. (number? markup?).
103 markup-command may be a procedure."
104   (let ((sig-key (hashq-ref markup-command-signatures
105                             markup-command)))
106     (if sig-key (car sig-key) #f)))
107
108 (define-public (markup-command-keyword markup-command)
109   "Return markup-command's keyword, e.g. \"scheme0markup1\".
110 markup-command may be a procedure."
111   (let ((sig-key (hashq-ref markup-command-signatures
112                             markup-command)))
113     (if sig-key (cdr sig-key) #f)))
114
115 (define (markup-command-signatureset! markup-command signature)
116   "Set markup-command's signature. markup-command must be a named procedure.
117 Also set markup-signature and markup-keyword object properties."
118   (hashq-set! markup-command-signatures
119               markup-command
120               (cons signature (markup-signature-to-keyword signature)))
121   ;; these object properties are still in use somewhere
122   (set-object-property! markup-command 'markup-signature signature)
123   (set-object-property! markup-command 'markup-keyword (markup-signature-to-keyword signature)))
124   
125 (define-public markup-command-signature
126   (make-procedure-with-setter markup-command-signature-ref markup-command-signatureset!))
127
128 (define (markup-symbol-to-proc markup-sym)
129   "Return the markup command procedure which name is `markup-sym', if any."
130   (hash-fold (lambda (key val prev)
131                             (or prev
132                                 (if (eqv? (procedure-name key) markup-sym) key #f)))
133              #f
134              markup-command-signatures))
135
136 (define-public markup-function-list '())
137
138 (define-public (markup-signature-to-keyword sig)
139   " (A B C) -> a0-b1-c2 "
140   (if (null? sig)
141       'empty
142       (string->symbol (string-join (map
143                                     (let* ((count 0))
144                                       (lambda (func)
145                                         (set! count (+ count 1))
146                                         (string-append
147                                          ;; for reasons I don't get,
148                                          ;; (case func ((markup?) .. )
149                                          ;; doesn't work.
150                                          (cond 
151                                           ((eq? func markup?) "markup")
152                                           ((eq? func markup-list?) "markup-list")
153                                           (else "scheme"))
154                                          (number->string (- count 1)))))
155                                     sig)
156                          "-"))))
157
158 (define-public (lookup-markup-command code)
159   (let ((proc (markup-symbol-to-proc (string->symbol (string-append code "-markup")))))
160     (and proc (cons proc (markup-command-keyword proc)))))
161
162 ;;;;;;;;;;;;;;;;;;;;;;
163 ;;; markup type predicates
164
165 (define (markup-function? x)
166   (not (not (markup-command-signature x))))
167
168 (define (markup-list? arg)
169   (define (markup-list-inner? l)
170     (or (null? l)
171         (and (markup? (car l)) (markup-list-inner? (cdr l)))))
172   (and (list? arg) (markup-list-inner? arg)))
173
174 (define (markup-argument-list? signature arguments)
175   "Typecheck argument list."
176   (if (and (pair? signature) (pair? arguments))
177       (and ((car signature) (car arguments))
178            (markup-argument-list? (cdr signature) (cdr arguments)))
179       (and (null? signature) (null? arguments))))
180
181
182 (define (markup-argument-list-error signature arguments number)
183   "return (ARG-NR TYPE-EXPECTED ARG-FOUND) if an error is detected, or
184 #f is no error found.
185 "
186   (if (and (pair? signature) (pair? arguments))
187       (if (not ((car signature) (car arguments)))
188           (list number (type-name (car signature)) (car arguments))
189           (markup-argument-list-error (cdr signature) (cdr arguments) (+ 1 number)))
190       #f))
191
192 ;;
193 ;; full recursive typecheck.
194 ;;
195 (define (markup-typecheck? arg)
196   (or (string? arg)
197       (and (pair? arg)
198            (markup-function? (car arg))
199            (markup-argument-list? (markup-command-signature (car arg))
200                                   (cdr arg)))))
201
202 ;; 
203 ;; typecheck, and throw an error when something amiss.
204 ;; 
205 (define (markup-thrower-typecheck arg)
206   (cond ((string? arg) #t)
207         ((not (pair? arg))
208          (throw 'markup-format "Not a pair" arg))
209         ((not (markup-function? (car arg)))
210          (throw 'markup-format "Not a markup function " (car arg)))
211         ((not (markup-argument-list? (markup-command-signature (car arg))
212                                      (cdr arg)))
213          (throw 'markup-format "Arguments failed  typecheck for " arg)))
214   #t)
215
216 ;;
217 ;; good enough if you only  use make-XXX-markup functions.
218 ;; 
219 (define (cheap-markup? x)
220   (or (string? x)
221       (and (pair? x)
222            (markup-function? (car x)))))
223
224 ;;
225 ;; replace by markup-thrower-typecheck for more detailed diagnostics.
226 ;; 
227 (define-public markup? cheap-markup?)
228
229 ;; utility
230
231 (define (markup-join markups sep)
232   "Return line-markup of MARKUPS, joining them with markup SEP"
233   (if (pair? markups)
234       (make-line-markup (list-insert-separator markups sep))
235       empty-markup))
236
237
238 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
239 ;;; markup commands
240 ;; TODO:
241 ;; each markup function should have a doc string with
242 ;; syntax, description and example. 
243 ;;
244
245 (define-public brew-new-markup-molecule Text_item::brew_molecule)
246
247 (define-public interpret-markup Text_item::interpret_markup)
248
249 (def-markup-command (simple paper props str) (string?)
250   "A simple text-string; @code{\\markup @{ foo @}} is equivalent with
251 @code{\\markup @{ \\simple #\"foo\" @}}.
252 "
253   (interpret-markup paper props str))
254
255 (define-public empty-markup (make-simple-markup ""))
256
257 (define-public (stack-molecule-line space molecules)
258   (if (pair? molecules)
259       (if (pair? (cdr molecules))
260           (let* ((tail (stack-molecule-line  space (cdr molecules)))
261                  (head (car molecules))
262                  (xoff (+ space (cdr (ly:molecule-get-extent head X)))))
263             (ly:molecule-add head
264                              (ly:molecule-translate-axis tail xoff X)))
265           (car molecules))
266       '()))
267
268 (def-markup-command (line paper props markps) (markup-list?)
269   "A horizontal line of markups. Syntax:
270 \\line << MARKUPS >>
271 "
272   (stack-molecule-line
273    (cdr (chain-assoc 'word-space props))
274    (map (lambda (m) (interpret-markup paper props m)) markps)))
275
276 (def-markup-command (combine paper props m1 m2) (markup? markup?)
277   "Overstrike two markups."
278   (ly:molecule-add
279    (interpret-markup paper props m1)
280    (interpret-markup paper props m2)))
281
282 (def-markup-command (finger paper props arg) (markup?)
283   (interpret-markup paper
284                     (cons '((font-size . -4) (font-family . number)) props)
285                     arg))
286
287 (define-public (set-property-markup qualifier)
288   (lambda (paper props qualifier-val markp)
289     (interpret-markup paper
290                       (cons (cons `(,qualifier . ,qualifier-val) (car props)) (cdr props))
291                       markp)))
292
293 (def-markup-command fontsize (number? markup?)
294   (set-property-markup 'font-size))
295
296 (def-markup-command magnify (number? markup?)
297   (set-property-markup 'font-magnification))
298
299 (define (font-markup qualifier value)
300   (lambda (paper props markp)
301     (interpret-markup paper
302                       (cons (cons `(,qualifier . ,value) (car props)) (cdr props))
303                       markp)))
304
305 (def-markup-command bold (markup?)
306   (font-markup 'font-series 'bold))
307
308 (def-markup-command sans (markup?)
309   (font-markup 'font-family 'sans))
310
311 (def-markup-command number (markup?)
312   (font-markup 'font-family 'number))
313
314 (def-markup-command roman (markup?)
315   (font-markup 'font-family 'roman))
316
317 (def-markup-command huge (markup?)
318   (font-markup 'font-size 2))
319
320 (def-markup-command large (markup?)
321   (font-markup 'font-size 1))
322
323 (def-markup-command normalsize (markup?)
324   (font-markup 'font-size 0))
325
326 (def-markup-command small (markup?)
327   (font-markup 'font-size -1))
328
329 (def-markup-command tiny (markup?)
330   (font-markup 'font-size -2))
331
332 (def-markup-command teeny (markup?)
333   (font-markup 'font-size -3))
334
335 (def-markup-command dynamic (markup?)
336   (font-markup 'font-family 'dynamic))
337
338 (def-markup-command italic (markup?)
339   (font-markup 'font-shape 'italic))
340
341 (def-markup-command typewriter (markup?)
342   (font-markup 'font-family 'typewriter))
343
344 (def-markup-command (column paper props mrkups) (markup-list?)
345   (stack-lines
346    -1 0.0 (cdr (chain-assoc 'baseline-skip props))
347    (map (lambda (m) (interpret-markup paper props m)) mrkups)))
348
349 (def-markup-command (dir-column paper props mrkups) (markup-list?)
350   "Make a column of args, going up or down, depending on the setting
351 of the #'direction layout property."
352   (let* ((dir (cdr (chain-assoc 'direction props))))
353     (stack-lines
354      (if (number? dir) dir -1)
355      0.0
356      (cdr (chain-assoc 'baseline-skip props))
357      (map (lambda (x) (interpret-markup paper props x)) mrkups))))
358
359 (def-markup-command (center paper props mrkups) (markup-list?)
360   (let* ((mols (map (lambda (x) (interpret-markup paper props x)) mrkups))
361          (cmols (map (lambda (x) (ly:molecule-align-to! x X CENTER)) mols)))
362     (stack-lines -1 0.0 (cdr (chain-assoc 'baseline-skip props)) mols)))
363
364 (def-markup-command (right-align paper props mrkup) (markup?)
365   (let* ((m (interpret-markup paper props mrkup)))
366     (ly:molecule-align-to! m X RIGHT)
367     m))
368
369 (def-markup-command (left-align paper props mrkup) (markup?)
370   (let* ((m (interpret-markup paper props mrkup)))
371     (ly:molecule-align-to! m X LEFT)
372     m))
373
374 (def-markup-command (halign paper props dir mrkup) (number? markup?)
375   "Set horizontal alignment. Syntax: halign A MARKUP. A=-1 is LEFT,
376 A=1 is right, values in between vary alignment accordingly."
377   (let* ((m (interpret-markup paper props mrkup)))
378     (ly:molecule-align-to! m X dir)
379     m))
380
381 (def-markup-command (musicglyph paper props glyph-name) (string?)
382   (ly:find-glyph-by-name
383    (ly:paper-get-font paper (cons '((font-name . ())
384                                     (font-shape . *)
385                                     (font-series . *)
386                                     (font-family . music))
387                                   props))
388    glyph-name))
389
390
391 (def-markup-command (lookup paper props glyph-name) (string?)
392   "Lookup a glyph by name."
393   (ly:find-glyph-by-name (ly:paper-get-font paper props)
394                          glyph-name))
395
396 (def-markup-command (char paper props num) (integer?)
397   "Syntax: \\char NUMBER. "
398   (ly:get-glyph (ly:paper-get-font paper props) num))
399
400 (def-markup-command (raise paper props amount mrkup) (number? markup?)
401   "Syntax: \\raise AMOUNT MARKUP. "
402   (ly:molecule-translate-axis (interpret-markup paper props mrkup)
403                               amount Y))
404
405 (def-markup-command (fraction paper props mrkup1 mrkup2) (markup? markup?)
406   "Make a fraction of two markups.
407
408 Syntax: \\fraction MARKUP1 MARKUP2."
409   (let* ((m1 (interpret-markup paper props mrkup1))
410          (m2 (interpret-markup paper props mrkup2)))
411     (ly:molecule-align-to! m1 X CENTER)
412     (ly:molecule-align-to! m2 X CENTER)    
413     (let* ((x1 (ly:molecule-get-extent m1 X))
414            (x2 (ly:molecule-get-extent m2 X))
415            (line (ly:round-filled-box (interval-union x1 x2) '(-0.05 . 0.05) 0.0))
416            ;; should stack mols separately, to maintain LINE on baseline
417            (stack (stack-lines -1 0.2 0.6 (list m1 line m2))))
418       (ly:molecule-align-to! stack Y CENTER)
419       (ly:molecule-align-to! stack X LEFT)
420       ;; should have EX dimension
421       ;; empirical anyway
422       (ly:molecule-translate-axis stack 0.75 Y))))
423
424
425 ;; TODO: better syntax.
426
427 (def-markup-command (note-by-number paper props log dot-count dir) (number? number? number?)
428   "Syntax: \\note-by-number #LOG #DOTS #DIR.  By using fractional values
429 for DIR, you can obtain longer or shorter stems."
430   (let* ((font (ly:paper-get-font paper (cons '((font-family .  music)) props)))
431          (stemlen (max 3 (- log 1)))
432          (headgl (ly:find-glyph-by-name
433                   font
434                   (string-append "noteheads-" (number->string (min log 2)))))
435          (stemth 0.13)
436          (stemy (* dir stemlen))
437          (attachx (if (> dir 0)
438                       (- (cdr (ly:molecule-get-extent headgl X)) stemth)
439                       0))
440          (attachy (* dir 0.28))
441          (stemgl (and (> log 0)
442                       (ly:round-filled-box
443                        (cons attachx (+ attachx  stemth))
444                        (cons (min stemy attachy)
445                              (max stemy attachy))
446                        (/ stemth 3))))
447          (dot (ly:find-glyph-by-name font "dots-dot"))
448          (dotwid (interval-length (ly:molecule-get-extent dot X)))
449          (dots (and (> dot-count 0)
450                     (apply ly:molecule-add
451                            (map (lambda (x)
452                                   (ly:molecule-translate-axis
453                                    dot  (* (+ 1 (* 2 x)) dotwid) X) )
454                                 (iota dot-count 1)))))
455          (flaggl (and (> log 2)
456                       (ly:molecule-translate
457                        (ly:find-glyph-by-name font
458                                               (string-append "flags-"
459                                                              (if (> dir 0) "u" "d")
460                                                              (number->string log)))
461                        (cons (+ attachx (/ stemth 2)) stemy)))))
462     (if flaggl
463         (set! stemgl (ly:molecule-add flaggl stemgl)))
464     (if (ly:molecule? stemgl)
465         (set! stemgl (ly:molecule-add stemgl headgl))
466         (set! stemgl headgl))
467     (if (ly:molecule? dots)
468         (set! stemgl
469               (ly:molecule-add
470                (ly:molecule-translate-axis dots
471                                            (+ (if (and (> dir 0) (> log 2))
472                                                   (* 1.5 dotwid)
473                                                   0)
474                                               ;; huh ? why not necessary?
475                                               ;;(cdr (ly:molecule-get-extent headgl X))
476                                               dotwid)
477                                            X)
478                stemgl)))
479     stemgl))
480
481 (use-modules (ice-9 regex))
482
483 (define-public log2 
484   (let ((divisor (log 2)))
485     (lambda (z) (inexact->exact (/ (log z) divisor)))))
486
487 (define (parse-simple-duration duration-string)
488   "Parse the `duration-string', eg ''4..'' or ''breve.'', and return a (log dots) list."
489   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
490     (if (and match (string=? duration-string (match:substring match 0)))
491         (let ((len  (match:substring match 1))
492               (dots (match:substring match 2)))
493           (list (cond ((string=? len "breve")  -1)
494                       ((string=? len "longa")  -2)
495                       ((string=? len "maxima") -3)
496                       (else (log2 (string->number len))))
497                 (if dots (string-length dots) 0)))
498         (error "This is not a valid duration string:" duration-string))))
499
500 (def-markup-command (note paper props duration-string dir) (string? number?)
501   "This produces a note with a stem pointing in @var{dir} direction, with
502 the @var{duration} for the note head type and augmentation dots. For
503 example, @code{\note #\"4.\" #-0.75} creates a dotted quarter note, with
504 a shortened down stem."
505   (let ((parsed (parse-simple-duration duration-string)))
506     (note-by-number-markup paper props (car parsed) (cadr parsed) dir)))
507
508 (def-markup-command (normal-size-super paper props mrkup) (markup?)
509   (ly:molecule-translate-axis (interpret-markup
510                                paper
511                                props mrkup)
512                               (* 0.5 (cdr (chain-assoc 'baseline-skip props)))
513                               Y))
514
515 (def-markup-command (super paper props mrkup) (markup?)
516   "Syntax: \\super MARKUP. "
517   (ly:molecule-translate-axis
518    (interpret-markup
519     paper
520     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
521     mrkup)
522    (* 0.5 (cdr (chain-assoc 'baseline-skip props)))
523    Y))
524
525 (def-markup-command (translate paper props offset mrkup) (number-pair? markup?)
526   "Syntax: \\translate OFFSET MARKUP. "
527   (ly:molecule-translate (interpret-markup  paper props mrkup)
528                          offset))
529
530 (def-markup-command (sub paper props mrkup) (markup?)
531   "Syntax: \\sub MARKUP."
532   (ly:molecule-translate-axis
533    (interpret-markup
534     paper
535     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
536     mrkup)
537    (* -0.5 (cdr (chain-assoc 'baseline-skip props)))
538    Y))
539
540 (def-markup-command (normal-size-sub paper props mrkup) (markup?)
541   (ly:molecule-translate-axis
542    (interpret-markup paper props mrkup)
543    (* -0.5 (cdr (chain-assoc 'baseline-skip props)))
544    Y))
545
546 (def-markup-command (hbracket paper props mrkup) (markup?)
547   "Horizontal brackets around its single argument. Syntax \\hbracket MARKUP."  
548   (let ((th 0.1) ;; todo: take from GROB.
549         (m (interpret-markup paper props mrkup)))
550     (bracketify-molecule m X th (* 2.5 th) th)))
551
552 (def-markup-command (bracket paper props mrkup) (markup?)
553   "Vertical brackets around its single argument. Syntax \\bracket MARKUP."  
554   (let ((th 0.1) ;; todo: take from GROB.
555         (m (interpret-markup paper props mrkup)))
556     (bracketify-molecule m Y th (* 2.5 th) th)))
557
558 ;; todo: fix negative space
559 (def-markup-command (hspace paper props amount) (number?)
560   "Syntax: \\hspace NUMBER."
561   (if (> amount 0)
562       (ly:make-molecule "" (cons 0 amount) '(-1 . 1) )
563       (ly:make-molecule "" (cons amount amount) '(-1 . 1))))
564
565 (def-markup-command (override paper props new-prop mrkup) (pair? markup?)
566   "Add the first argument in to the property list.  Properties may be
567 any sort of property supported by @ref{font-interface} and
568 @ref{text-interface}, for example
569
570 \\override #'(font-family . married) \"bla\"
571 "
572   (interpret-markup paper (cons (list new-prop) props) mrkup))
573
574 (def-markup-command (smaller paper props mrkup) (markup?)
575   "Syntax: \\smaller MARKUP"
576   (let* ((fs (chain-assoc-get 'font-size props 0))
577          (entry (cons 'font-size (- fs 1))))
578     (interpret-markup paper (cons (list entry) props) mrkup)))
579
580
581 (def-markup-command (bigger paper props mrkup) (markup?)
582   "Syntax: \\bigger MARKUP"
583   (let* ((fs (chain-assoc-get 'font-size props 0))
584          (entry (cons 'font-size (+ fs 1))))
585     (interpret-markup paper (cons (list entry) props) mrkup)))
586
587 (def-markup-command larger (markup?)
588   bigger-markup)
589
590 (def-markup-command (box paper props mrkup) (markup?)
591   "Syntax: \\box MARKUP"
592   (let ((th 0.1)
593         (pad 0.2)
594         (m (interpret-markup paper props mrkup)))
595     (box-molecule m th pad)))
596
597 (def-markup-command (strut paper props) ()
598   "Syntax: \\strut
599
600  A box of the same height as the space.
601 "
602   (let ((m (Text_item::interpret_markup paper props " ")))
603     (ly:molecule-set-extent! m X '(1000 . -1000))
604     m))
605
606 (define number->mark-letter-vector (make-vector 25 #\A))
607
608 (do ((i 0 (1+ i))
609      (j 0 (1+ j)))
610     ((>= i 26))
611   (if (= i (- (char->integer #\I) (char->integer #\A)))
612       (set! i (1+ i)))
613   (vector-set! number->mark-letter-vector j
614                (integer->char (+ i (char->integer #\A)))))
615
616 (define (number->markletter-string n)
617   "Double letters for big marks."
618   (let*
619       ((l (vector-length number->mark-letter-vector)))
620     
621   (if (>= n l)
622       (string-append (number->markletter-string (1- (quotient n l)))
623                      (number->markletter-string (remainder n l)))
624       (make-string 1 (vector-ref number->mark-letter-vector n)))))
625
626
627 (def-markup-command (markletter paper props num) (number?)
628   "Markup letters: skip I and do double letters for big marks.
629 Syntax: \\markletter #25"
630   (Text_item::interpret_markup paper props (number->markletter-string num)))
631
632 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
633
634 (if #f
635     (define (typecheck-with-error x)
636       (catch
637        'markup-format
638        (lambda () (markup? x))
639        (lambda (key message arg)
640          (display "\nERROR: markup format error: \n")
641          (display message)
642          (newline)
643          (write arg (current-output-port))))))
644
645 ;; test make-foo-markup functions
646 (if #f
647     (begin
648       (newline)
649       (newline)
650       (display (make-line-markup (list (make-simple-markup "FOO"))))
651       
652       (make-line-markup (make-simple-markup "FOO"))
653       (make-line-markup (make-simple-markup "FOO") (make-simple-markup "foo"))
654       (make-raise-markup "foo" (make-simple-markup "foo"))))
655
656 ;;
657 ;; test typecheckers. Not wholly useful, because errors are detected
658 ;; in other places than they're made.
659 ;;
660 (if #f
661     (begin
662       ;; To get error messages, see above to install the alternate
663       ;; typecheck routine for markup?.
664       (display (typecheck-with-error `(,simple-markup "foobar")))
665       (display (typecheck-with-error `(,simple-markup "foobar")))
666       (display (typecheck-with-error `(,simple-markup 1)))
667       (display
668        (typecheck-with-error `(,line-markup ((,simple-markup "foobar"))
669                                             (,simple-markup 1))))
670       (display
671        (typecheck-with-error `(,line-markup (,simple-markup "foobar")
672                                             (,simple-markup "bla"))))))