1 ;;;; framework-tex.scm -- structure for TeX output
3 ;;;; source file of the GNU LilyPond music typesetter
5 ;;;; (c) 2004--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 (define-module (scm framework-tex)
8 #:export (output-framework-tex
9 output-classic-framework-tex))
11 (use-modules (ice-9 regex)
22 (define format ergonomic-simple-format)
24 (define (output-formats)
25 (define formats (ly:output-formats))
26 (set! formats (completize-formats formats))
27 (if (member "ps" formats)
28 (set! formats (cons "dvi" formats)))
29 (if (member "dvi" formats)
30 (set! formats (cons "tex" formats)))
34 (define framework-tex-module (current-module))
35 (define-public (sanitize-tex-string s)
36 (if (ly:get-option 'safe)
37 (regexp-substitute/global
39 (regexp-substitute/global #f "([{}])" s 'pre "\\" 1 'post)
40 'pre "$\\backslash$" 'post)
43 (define (symbol->tex-key sym)
44 (regexp-substitute/global
45 #f "_" (sanitize-tex-string (symbol->string sym)) 'pre "X" 'post))
47 (define (tex-number-def prefix key number)
49 "\\def\\" prefix (symbol->tex-key key) "{" number "}%\n"))
51 (define-public (digits->letters str)
52 (regexp-substitute/global
54 (regexp-substitute/global
61 (+ (char->integer #\A)
62 (- (char->integer #\0))
63 (char->integer (string-ref (match:substring match 1) 0)))
69 (define-public (tex-font-command-raw name magnification)
72 (digits->letters (format "~a" name))
74 (string-encode-integer
75 (inexact->exact (round (* 1000 magnification))))))
77 (define-public (tex-font-command font)
79 (ly:font-file-name font) (ly:font-magnification font)))
81 (define (otf-font-load-command paper font)
82 (let* ((sub-fonts (ly:font-sub-fonts font)))
87 (format "\\font\\~a=~a scaled ~a%\n"
89 sub-name (ly:font-magnification font))
94 (ly:font-magnification font)
95 (ly:paper-output-scale paper)))))))
98 (define (simple-font-load-command paper font)
100 "\\font\\~a=~a scaled ~a%\n"
101 (tex-font-command font)
102 (ly:font-file-name font)
105 (ly:font-magnification font)
106 (ly:paper-output-scale paper))))))
108 (define (font-load-command paper font)
109 (if (pair? (ly:font-sub-fonts font))
110 (otf-font-load-command paper font)
111 (simple-font-load-command paper font)))
113 (define (define-fonts paper)
116 "\\def\\lilypondpaperunit{mm}%\n"
117 (tex-number-def "lilypondpaper" 'output-scale
118 (number->string (exact->inexact
119 (ly:paper-output-scale paper))))
120 (tex-string-def "lilypondpaper" 'papersize
121 (eval 'papersizename (ly:output-def-scope paper)))
123 (tex-string-def "lilypondpaper" 'input-encoding
124 (eval 'input-encoding (ly:output-def-scope paper)))
127 (map (lambda (x) (font-load-command paper x))
128 (ly:paper-fonts paper)))))
130 (define (tex-string-def prefix key str)
131 (if (equal? "" (sans-surrounding-whitespace (sanitize-tex-string str)))
132 (string-append "\\let\\" prefix (symbol->tex-key key) "\\undefined%\n")
133 (string-append "\\def\\" prefix (symbol->tex-key key)
134 "{" (sanitize-tex-string str) "}%\n")))
136 (define (header paper page-count classic?)
137 (let ((scale (ly:output-def-lookup paper 'output-scale))
138 (texpaper (string-append
139 (ly:output-def-lookup paper 'papersizename)
141 (landscape? (eq? #t (ly:output-def-lookup paper 'landscape))))
143 "% Generated by LilyPond "
144 (lilypond-version) "\n"
145 "% at " "time-stamp,FIXME" "\n"
147 (tex-string-def "lilypond" 'classic "1")
150 (if (ly:get-option 'safe)
155 "lilypondpaper" 'line-width
156 (ly:number->string (* scale (ly:output-def-lookup paper 'line-width))))
157 "\\def\\lilyponddocumentclassoptions{"
158 (sanitize-tex-string texpaper)
159 (if landscape? ",landscape" "")
165 "\\def\\scaletounit{ "
166 (number->string lily-unit->bigpoint-factor)
168 "\\ifx\\lilypondstart\\undefined\n"
169 " \\input lilyponddefs\n"
172 "\\lilypondspecial\n"
173 "\\lilypondpostscript\n"))
175 (define (dump-page putter page last? with-extents?)
176 (ly:outputter-dump-string
178 (format "\\lybox{~a}{~a}{%\n"
180 (interval-start (ly:stencil-extent page X))
183 (- (interval-start (ly:stencil-extent page Y)))
185 (ly:outputter-dump-stencil putter page)
186 (ly:outputter-dump-string
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 (open-file filename "wb") 'tex))
195 (paper (ly:paper-book-paper book))
196 (page-stencils (map page-stencil (ly:paper-book-pages book)))
197 (last-page (car (last-pair pages)))
199 (eq? #t (ly:output-def-lookup paper 'dump-extents))))
202 (ly:outputter-dump-string outputter x))
204 (header paper (length page-stencils) #f)
207 (ly:outputter-dump-string outputter "\\lilypondnopagebreak\n")
210 (dump-page outputter page (eq? last-page page) with-extents))
212 (ly:outputter-dump-string outputter "\\lilypondend\n")
213 (ly:outputter-close outputter)
214 (postprocess-output book framework-tex-module filename
217 (define (dump-line putter line last?)
218 (ly:outputter-dump-string
220 (format "\\lybox{~a}{~a}{%\n"
222 (max 0 (interval-end (paper-system-extent line X))))
224 (interval-length (paper-system-extent line Y)))))
226 (ly:outputter-dump-stencil putter (paper-system-stencil line))
227 (ly:outputter-dump-string
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
237 (open-file filename "w") 'tex))
238 (paper (ly:paper-book-paper book))
239 (lines (ly:paper-book-systems book))
240 (last-line (car (last-pair lines))))
243 (ly:outputter-dump-string outputter x))
246 (header paper (length lines) #f)
247 "\\def\\lilypondclassic{1}%\n"
248 (output-scopes scopes fields basename)
253 (lambda (line) (dump-line outputter line (eq? line last-line))) lines)
254 (ly:outputter-dump-string outputter "\\lilypondend\n")
255 (ly:outputter-close outputter)
256 (postprocess-output book framework-tex-module filename
260 (define-public (output-preview-framework
261 basename book scopes fields)
262 (let* ((filename (format "~a.tex" basename))
263 (outputter (ly:make-paper-outputter (open-file filename "wb")
265 (paper (ly:paper-book-paper book))
266 (lines (ly:paper-book-systems book))
267 (first-notes-index (list-index
268 (lambda (s) (not (ly:paper-system-title? s)))
273 (ly:outputter-dump-string outputter x))
277 (header paper (length lines) #f)
278 "\\def\\lilypondclassic{1}%\n"
279 (output-scopes scopes fields basename)
285 (dump-line outputter lst (not (ly:paper-system-title? lst))))
286 (take lines (1+ first-notes-index)))
287 (ly:outputter-dump-string outputter "\\lilypondend\n")
288 (ly:outputter-close outputter)
289 (postprocess-output book framework-tex-module filename
292 (define-public (convert-to-pdf book name)
293 (let* ((defs (ly:paper-book-paper book))
294 (paper-width (ly:output-def-lookup defs 'paper-width))
295 (paper-height (ly:output-def-lookup defs 'paper-height))
296 (output-scale (ly:output-def-lookup defs 'output-scale)))
297 (postscript->pdf (* paper-width output-scale (/ (ly:bp 1)))
298 (* paper-height output-scale (/ (ly:bp 1)))
299 (string-append (dir-basename name ".tex") ".ps"))))
301 (define-public (convert-to-png book name)
302 (let* ((defs (ly:paper-book-paper book))
303 (resolution (ly:output-def-lookup defs 'pngresolution))
304 (paper-width (ly:output-def-lookup defs 'paper-width))
305 (paper-height (ly:output-def-lookup defs 'paper-height))
306 (output-scale (ly:output-def-lookup defs 'output-scale)))
308 (if (number? resolution)
310 (ly:get-option 'resolution))
312 (* paper-width output-scale (/ (ly:bp 1)))
313 (* paper-height output-scale (/ (ly:bp 1)))
315 (string-append (dir-basename name ".tex") ".ps"))))
317 (define-public (convert-to-ps book name)
318 (let* ((paper (ly:paper-book-paper book))
319 (preview? (string-contains name ".preview"))
320 (papersizename (ly:output-def-lookup paper 'papersizename))
321 (landscape? (eq? #t (ly:output-def-lookup paper 'landscape)))
322 (base (dir-basename name ".tex"))
323 (ps-name (format "~a.ps" base ".ps"))
324 (cmd (string-append "dvips"
329 ;; careful: papersizename is user-set.
330 (sanitize-command-option papersizename)
332 (if landscape? " -tlandscape" "")
333 (if (ly:kpathsea-find-file "lm.map")
335 (if (ly:kpathsea-find-file "ecrm10.pfa")
336 " -u+ec-mftrace.map" "")
337 " -u+lilypond.map -Ppdf" ""
340 (if (access? ps-name W_OK)
341 (delete-file ps-name))
342 (if (not (ly:get-option 'verbose))
344 (ly:message (_ "Converting to `~a'...") (string-append base ".ps"))
348 (define-public (convert-to-dvi book name)
349 (let* ((curr-extra-mem
351 (regexp-substitute/global
353 (ly:kpathsea-expand-variable "extra_mem_top")
355 (base (dir-basename name ".tex"))
357 #f "latex \\\\nonstopmode \\\\input '~a'" name)))
359 ;; FIXME: latex 'foo bar' works, but \input 'foe bar' does not?
360 (if (string-index name (char-set #\space #\ht #\newline #\cr))
361 (ly:error (_"TeX file name must not contain whitespace: `~a'") name))
363 (setenv "extra_mem_top" (number->string (max curr-extra-mem 1024000)))
364 (let ((dvi-name (string-append base ".dvi")))
365 (if (access? dvi-name W_OK)
366 (delete-file dvi-name)))
367 (if (not (ly:get-option 'verbose))
369 (ly:message (_ "Converting to `~a'...") (string-append base ".dvi"))
372 ;; FIXME: set in environment?
373 (if (ly:get-option 'safe)
374 (set! cmd (string-append "openout_any=p " cmd)))
378 (define-public (convert-to-tex book name)