release commit
[lilypond.git] / scm / framework-tex.scm
blob441fcd557d92d4f0169ef99b036ba377514568ae
1 ;;
2 ;; framework-tex.scm -- structure for TeX output
3 ;;
4 ;; source file of the GNU LilyPond music typesetter
5 ;;
6 ;; (c) 2004--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
8 (define-module (scm framework-tex)
9   #:export (output-framework-tex        
10             output-classic-framework-tex))
12 (use-modules (ice-9 regex)
13              (ice-9 string-fun)
14              (ice-9 format)
15              (guile)
16              (srfi srfi-1)
17              (srfi srfi-13)
18              (srfi srfi-14)
19              (lily))
21 (define (output-formats)
22   (define formats (ly:output-formats))
23   (set! formats (completize-formats formats))
24   (if (member "ps" formats)
25       (set! formats (cons "dvi" formats)))
26   (if (member "dvi" formats)
27       (set! formats (cons "tex" formats)))
29   (uniq-list formats))
31 (define framework-tex-module (current-module))
32 (define-public (sanitize-tex-string s)
33   (if (ly:get-option 'safe)
34       (regexp-substitute/global
35        #f "\\\\"
36        (regexp-substitute/global #f "([{}])" s 'pre  "\\" 1 'post)
37        'pre "$\\backslash$" 'post)
38       s))
40 (define (symbol->tex-key sym)
41   (regexp-substitute/global
42    #f "_" (sanitize-tex-string (symbol->string sym)) 'pre "X" 'post))
44 (define (tex-number-def prefix key number)
45   (string-append
46    "\\def\\" prefix (symbol->tex-key key) "{" number "}%\n"))
48 (define-public (digits->letters str)
49   (regexp-substitute/global
50    #f "[-\\._]"
51    (regexp-substitute/global
52     #f "([0-9])" str
53     'pre
54     (lambda (match)
55       (make-string
56        1
57        (integer->char
58         (+ (char->integer #\A)
59            (- (char->integer #\0))
60            (char->integer (string-ref (match:substring match 1) 0)))
61         )))
62     'post)
63    'pre ""
64    'post))
66 (define-public (tex-font-command-raw name magnification)
67   (string-append
68    "magfont"
69    (digits->letters (format "~a" name))
70    "m"
71    (string-encode-integer
72     (inexact->exact (round (* 1000 magnification))))))
74 (define-public (tex-font-command font)
75   (tex-font-command-raw
76    (ly:font-file-name font) (ly:font-magnification font)))
78 (define (otf-font-load-command paper font)
79   (let* ((sub-fonts (ly:font-sub-fonts font)))
80     (string-append
81      (apply string-append
82             (map
83              (lambda (sub-name)
84                (format #f "\\font\\~a=~a scaled ~a%\n"
85                        (tex-font-command-raw
86                         sub-name (ly:font-magnification font))
87                        sub-name
88                        (ly:number->string
89                         (inexact->exact
90                          (round (* 1000
91                                    (ly:font-magnification font)
92                                    (ly:paper-outputscale paper)))))))
93              sub-fonts)))))
95 (define (simple-font-load-command paper font)
96    (format
97     "\\font\\~a=~a scaled ~a%\n"
98     (tex-font-command font)
99     (ly:font-file-name font)
100     (inexact->exact
101      (round (* 1000
102                (ly:font-magnification font)
103                (ly:paper-outputscale paper))))))
105 (define (font-load-command paper font)
106   (if (pair? (ly:font-sub-fonts font))
107       (otf-font-load-command paper font)
108       (simple-font-load-command paper font)))
110 (define (define-fonts paper)
111   (string-append
112    ;; UGH. FIXME.
113    "\\def\\lilypondpaperunit{mm}%\n"
114    (tex-number-def "lilypondpaper" 'outputscale
115                    (number->string (exact->inexact
116                                     (ly:paper-outputscale paper))))
117    (tex-string-def "lilypondpaper" 'papersize
118                    (eval 'papersizename (ly:output-def-scope paper)))
119    ;; paper/layout?
120    (tex-string-def "lilypondpaper" 'inputencoding
121                    (eval 'inputencoding (ly:output-def-scope paper)))
123    (apply string-append
124           (map (lambda (x) (font-load-command paper x))
125                (ly:paper-fonts paper)))))
127 (define (tex-string-def prefix key str)
128   (if (equal? "" (sans-surrounding-whitespace (sanitize-tex-string str)))
129       (string-append "\\let\\" prefix (symbol->tex-key key) "\\undefined%\n")
130       (string-append "\\def\\" prefix (symbol->tex-key key)
131                      "{" (sanitize-tex-string str) "}%\n")))
133 (define (header paper page-count classic?)
134   (let ((scale (ly:output-def-lookup paper 'outputscale))
135         (texpaper (string-append
136                    (ly:output-def-lookup paper 'papersizename)
137                    "paper"))
138         (landscape? (eq? #t (ly:output-def-lookup paper 'landscape))))
139     (string-append
140      "% Generated by LilyPond "
141      (lilypond-version) "\n"
142      "% at " "time-stamp,FIXME" "\n"
143      (if classic?
144          (tex-string-def "lilypond" 'classic "1")
145          "")
147      (if (ly:get-option 'safe)
148          "\\nofiles\n"
149          "")
151      (tex-string-def
152       "lilypondpaper" 'linewidth
153       (ly:number->string (* scale (ly:output-def-lookup paper 'linewidth))))
154      "\\def\\lilyponddocumentclassoptions{"
155      (sanitize-tex-string texpaper)
156      (if landscape? ",landscape" "")
157      "}%\n"
158      (tex-string-def
159       "lilypondpaper" 'interscoreline
160       (ly:number->string
161        (* scale (ly:output-def-lookup paper 'interscoreline)))))))
163 (define (header-end)
164   (string-append
165    "\\def\\scaletounit{ "
166    (number->string lily-unit->bigpoint-factor)
167    " mul }%\n"
168    "\\ifx\\lilypondstart\\undefined\n"
169    "  \\input lilyponddefs\n"
170    "\\fi\n"
171    "\\lilypondstart\n"
172    "\\lilypondspecial\n"
173    "\\lilypondpostscript\n"))
175 (define (dump-page putter page last? with-extents?)
176   (ly:outputter-dump-string
177    putter
178    (format "\\lybox{~a}{~a}{%\n"
179            (if with-extents?
180                (interval-start (ly:stencil-extent page X))
181                0.0)
182            (if with-extents?
183                (- (interval-start (ly:stencil-extent page Y)))
184                0.0)))
185   (ly:outputter-dump-stencil putter page)
186   (ly:outputter-dump-string
187    putter
188    (if last?
189        "}%\n\\vfill\n"
190        "}%\n\\vfill\n\\lilypondpagebreak\n")))
192 (define-public (output-framework basename book scopes fields)
193   (let* ((filename (format "~a.tex" basename))
194          (outputter  (ly:make-paper-outputter filename "tex"))
195          (paper (ly:paper-book-paper book))
196          (pages (ly:paper-book-pages book))
197          (last-page (car (last-pair pages)))
198          (with-extents
199           (eq? #t (ly:output-def-lookup paper 'dump-extents))))
200     (for-each
201      (lambda (x)
202        (ly:outputter-dump-string outputter x))
203      (list
204       (header paper (length pages) #f)
205       (define-fonts paper)
206       (header-end)))
207     (ly:outputter-dump-string outputter "\\lilypondnopagebreak\n")
208     (for-each
209      (lambda (page)
210        (dump-page outputter page (eq? last-page page) with-extents))
211      pages)
212     (ly:outputter-dump-string outputter "\\lilypondend\n")
213     (ly:outputter-close outputter)
214     (postprocess-output book framework-tex-module filename
215                         (output-formats))))
217 (define (dump-line putter line last?)
218   (ly:outputter-dump-string
219    putter
220    (format "\\lybox{~a}{~a}{%\n"
221            (ly:number->string
222             (max 0 (interval-end (ly:paper-system-extent line X))))
223            (ly:number->string
224             (interval-length (ly:paper-system-extent line Y)))))
226   (ly:outputter-dump-stencil putter (ly:paper-system-stencil line))
227   (ly:outputter-dump-string
228    putter
229    (if last?
230        "}%\n"
231        "}\\interscoreline\n")))
233 (define-public (output-classic-framework
234                 basename book scopes fields)
235   (let* ((filename (format "~a.tex" basename))
236          (outputter  (ly:make-paper-outputter filename "tex"))
237          (paper (ly:paper-book-paper book))
238          (lines (ly:paper-book-systems book))
239          (last-line (car (last-pair lines))))
240     (for-each
241      (lambda (x)
242        (ly:outputter-dump-string outputter x))
243      (list
244       ;;FIXME
245       (header paper (length lines) #f)
246       "\\def\\lilypondclassic{1}%\n"
247       (output-scopes scopes fields basename)
248       (define-fonts paper)
249       (header-end)))
251     (for-each
252      (lambda (line) (dump-line outputter line (eq? line last-line))) lines)
253     (ly:outputter-dump-string outputter "\\lilypondend\n")
254     (ly:outputter-close outputter)
255     (postprocess-output book framework-tex-module filename
256                         (output-formats))
257     ))
259 (define-public (output-preview-framework
260                 basename book scopes fields)
261   (let* ((filename (format "~a.tex" basename))
262          (outputter  (ly:make-paper-outputter filename
263                                               "tex"))
264          (paper (ly:paper-book-paper book))
265          (lines (ly:paper-book-systems book))
266          (first-notes-index (list-index
267                              (lambda (s) (not (ly:paper-system-title? s)))
268                              lines)))
270     (for-each
271      (lambda (x)
272        (ly:outputter-dump-string outputter x))
273      (list
274       
275       ;;FIXME
276       (header paper (length lines) #f)
277       "\\def\\lilypondclassic{1}%\n"
278       (output-scopes scopes fields basename)
279       (define-fonts paper)
280       (header-end)))
282     (for-each
283      (lambda (lst)
284        (dump-line outputter lst (not (ly:paper-system-title? lst))))
285      (take lines (1+ first-notes-index)))
286     (ly:outputter-dump-string outputter "\\lilypondend\n")
287     (ly:outputter-close outputter)
288     (postprocess-output book framework-tex-module filename
289                         (output-formats))))
291 (define-public (convert-to-pdf book name)
292   (let* ((defs (ly:paper-book-paper book))
293          (papersizename (ly:output-def-lookup defs 'papersizename)))
294     (postscript->pdf (if (string? papersizename) papersizename "a4")
295                      (string-append (basename name ".tex") ".ps"))))
297 (define-public (convert-to-png book name)
298   (let* ((defs (ly:paper-book-paper book))
299          (resolution (ly:output-def-lookup defs 'pngresolution))
300          (papersizename (ly:output-def-lookup defs 'papersizename)))
301     (postscript->png
302      (if (number? resolution)
303          resolution
304          (ly:get-option 'resolution))
306      (if (string? papersizename)
307          papersizename
308          "a4")
309      
310      (string-append (basename name ".tex") ".ps"))))
312 (define-public (convert-to-ps book name)
313   (let* ((paper (ly:paper-book-paper book))
314          (preview? (string-contains name ".preview"))
315          (papersizename (ly:output-def-lookup paper 'papersizename))
316          (landscape? (eq? #t (ly:output-def-lookup paper 'landscape)))
317          (base (basename name ".tex"))
318          (ps-name (format "~a.ps"  base ".ps"))
319          (cmd (string-append "dvips"
320                              (if preview?
321                                  " -E"
322                                  (string-append
323                                   " -t"
324                                   ;; careful: papersizename is user-set.
325                                   (sanitize-command-option papersizename)
326                                   ""))
327                              (if landscape? " -tlandscape" "")
328                              (if (ly:kpathsea-find-file "lm.map")
329                                  " -u+lm.map" "")
330                              (if (ly:kpathsea-find-file "ecrm10.pfa")
331                                  " -u+ec-mftrace.map" "")
332                              " -u+lilypond.map -Ppdf" ""
333                              " -o" ps-name
334                              " " base)))
335     (if (access? ps-name W_OK)
336         (delete-file ps-name))
337     (if (not (ly:get-option 'verbose))
338         (begin
339           (ly:message (_ "Converting to `~a'...") (string-append base ".ps"))
340           (ly:progress "\n")))
341     (ly:system cmd)))
343 (define-public (convert-to-dvi book name)
344   (let* ((curr-extra-mem
345           (string->number
346            (regexp-substitute/global
347             #f " *%.*\n?"
348             (ly:kpathsea-expand-variable "$extra_mem_top")
349             'pre "" 'post)))
350          (base (basename name ".tex"))
351          (cmd (format
352                #f "latex \\\\nonstopmode \\\\input '~a'" name)))
354     ;; FIXME: latex 'foo bar' works, but \input 'foe bar' does not?
355     (if (string-index name (char-set #\space #\ht #\newline #\cr))
356         (ly:error (_"TeX file name must not contain whitespace: `~a'") name))
358     (setenv "extra_mem_top" (number->string (max curr-extra-mem 1024000)))
359     (let ((dvi-name (string-append base ".dvi")))
360       (if (access? dvi-name W_OK)
361           (delete-file dvi-name)))
362     (if (not (ly:get-option 'verbose))
363         (begin
364           (ly:message (_ "Converting to `~a'...") (string-append base ".dvi"))
365           (ly:progress "\n")))
367     ;; FIXME: set in environment?
368     (if (ly:get-option 'safe)
369         (set! cmd (string-append "openout_any=p " cmd)))
371     (ly:system cmd)))
373 (define-public (convert-to-tex book name)
374   #t)