]> git.donarmstrong.com Git - lilypond.git/blob - python/book_latex.py
Lilypond-book: Factor out the formatting from lilypond-book into separate classes
[lilypond.git] / python / book_latex.py
1 # -*- coding: utf-8 -*-
2
3 import re
4 import tempfile
5 import os
6 import book_base as BookBase
7 from book_snippets import *
8 import lilylib as ly
9
10 # Recognize special sequences in the input.
11 #
12 #   (?P<name>regex) -- Assign result of REGEX to NAME.
13 #   *? -- Match non-greedily.
14 #   (?!...) -- Match if `...' doesn't match next (without consuming
15 #              the string).
16 #
17 #   (?m) -- Multiline regex: Make ^ and $ match at each line.
18 #   (?s) -- Make the dot match all characters including newline.
19 #   (?x) -- Ignore whitespace in patterns.
20 # Possible keys are:
21 #     'multiline_comment', 'verbatim', 'lilypond_block', 'singleline_comment',
22 #     'lilypond_file', 'include', 'lilypond', 'lilypondversion'
23 Latex_snippet_res = {
24     'include':
25          r'''(?smx)
26           ^[^%\n]*?
27           (?P<match>
28           \\input\s*{
29            (?P<filename>\S+?)
30           })''',
31
32     'lilypond':
33          r'''(?smx)
34           ^[^%\n]*?
35           (?P<match>
36           \\lilypond\s*(
37           \[
38            \s*(?P<options>.*?)\s*
39           \])?\s*{
40            (?P<code>.*?)
41           })''',
42
43     'lilypond_block':
44          r'''(?smx)
45           ^[^%\n]*?
46           (?P<match>
47           \\begin\s*(?P<env>{lilypond}\s*)?(
48           \[
49            \s*(?P<options>.*?)\s*
50           \])?(?(env)|\s*{lilypond})
51            (?P<code>.*?)
52           ^[^%\n]*?
53           \\end\s*{lilypond})''',
54
55     'lilypond_file':
56          r'''(?smx)
57           ^[^%\n]*?
58           (?P<match>
59           \\lilypondfile\s*(
60           \[
61            \s*(?P<options>.*?)\s*
62           \])?\s*\{
63            (?P<filename>\S+?)
64           })''',
65
66     'singleline_comment':
67          r'''(?mx)
68           ^.*?
69           (?P<match>
70            (?P<code>
71            %.*$\n+))''',
72
73     'verb':
74          r'''(?mx)
75           ^[^%\n]*?
76           (?P<match>
77            (?P<code>
78            \\verb(?P<del>.)
79             .*?
80            (?P=del)))''',
81
82     'verbatim':
83          r'''(?msx)
84           ^[^%\n]*?
85           (?P<match>
86            (?P<code>
87            \\begin\s*{verbatim}
88             .*?
89            \\end\s*{verbatim}))''',
90
91     'lilypondversion':
92          r'''(?smx)
93           (?P<match>
94           \\lilypondversion)[^a-zA-Z]''',
95 }
96
97 Latex_output = {
98     FILTER: r'''\begin{lilypond}[%(options)s]
99 %(code)s
100 \end{lilypond}''',
101
102     OUTPUT: r'''{%%
103 \parindent 0pt
104 \ifx\preLilyPondExample \undefined
105 \else
106   \expandafter\preLilyPondExample
107 \fi
108 \def\lilypondbook{}%%
109 \input %(base)s-systems.tex
110 \ifx\postLilyPondExample \undefined
111 \else
112   \expandafter\postLilyPondExample
113 \fi
114 }''',
115
116     PRINTFILENAME: '''\\texttt{%(filename)s}
117 ''',
118
119     QUOTE: r'''\begin{quotation}
120 %(str)s
121 \end{quotation}''',
122
123     VERBATIM: r'''\noindent
124 \begin{verbatim}%(verb)s\end{verbatim}
125 ''',
126
127     VERSION: r'''%(program_version)s''',
128 }
129
130
131
132
133
134 ###
135 # Retrieve dimensions from LaTeX
136 LATEX_INSPECTION_DOCUMENT = r'''
137 \nonstopmode
138 %(preamble)s
139 \begin{document}
140 \typeout{textwidth=\the\textwidth}
141 \typeout{columnsep=\the\columnsep}
142 \makeatletter\if@twocolumn\typeout{columns=2}\fi\makeatother
143 \end{document}
144 '''
145
146 # Do we need anything else besides `textwidth'?
147 def get_latex_textwidth (source, global_options):
148     m = re.search (r'''(?P<preamble>\\begin\s*{document})''', source)
149     if m == None:
150         warning (_ ("cannot find \\begin{document} in LaTeX document"))
151
152         ## what's a sensible default?
153         return 550.0
154
155     preamble = source[:m.start (0)]
156     latex_document = LATEX_INSPECTION_DOCUMENT % {'preamble': preamble}
157
158     (handle, tmpfile) = tempfile.mkstemp('.tex')
159     logfile = os.path.splitext (tmpfile)[0] + '.log'
160     logfile = os.path.split (logfile)[1]
161
162     tmp_handle = os.fdopen (handle,'w')
163     tmp_handle.write (latex_document)
164     tmp_handle.close ()
165
166     ly.system ('%s %s' % (global_options.latex_program, tmpfile),
167                be_verbose=global_options.verbose)
168     parameter_string = file (logfile).read()
169
170     os.unlink (tmpfile)
171     os.unlink (logfile)
172
173     columns = 0
174     m = re.search ('columns=([0-9.]+)', parameter_string)
175     if m:
176         columns = int (m.group (1))
177
178     columnsep = 0
179     m = re.search ('columnsep=([0-9.]+)pt', parameter_string)
180     if m:
181         columnsep = float (m.group (1))
182
183     textwidth = 0
184     m = re.search ('textwidth=([0-9.]+)pt', parameter_string)
185     if m:
186         textwidth = float (m.group (1))
187         if columns:
188             textwidth = (textwidth - columnsep) / columns
189
190     return textwidth
191
192
193 def modify_preamble (chunk):
194     str = chunk.replacement_text ()
195     if (re.search (r"\\begin *{document}", str)
196       and not re.search ("{graphic[sx]", str)):
197         str = re.sub (r"\\begin{document}",
198                r"\\usepackage{graphics}" + '\n'
199                + r"\\begin{document}",
200                str)
201         chunk.override_text = str
202
203
204
205
206
207
208 class BookLatexOutputFormat (BookBase.BookOutputFormat):
209     def __init__ (self):
210         BookBase.BookOutputFormat.__init__ (self)
211         self.format = "latex"
212         self.default_extension = ".tex"
213         self.snippet_res = Latex_snippet_res
214         self.output = Latex_output
215         self.handled_extensions = ['.latex', '.lytex', '.tex']
216         self.image_formats = "ps"
217         self.snippet_option_separator = '\s*,\s*'
218
219     def process_options (self, global_options):
220         self.process_options_pdfnotdefault (global_options)
221
222     def get_line_width (self, source):
223         textwidth = get_latex_textwidth (source, self.global_options)
224         return '%.0f\\pt' % textwidth
225
226     def input_fullname (self, input_filename):
227         # Use kpsewhich if available, otherwise fall back to the default:
228         if ly.search_exe_path ('kpsewhich'):
229             return os.popen ('kpsewhich ' + input_filename).read()[:-1]
230         else:
231             return BookBase.BookOutputFormat.input_fullname (self, input_filename)
232
233     def process_chunks (self, chunks):
234         for c in chunks:
235             if (c.is_plain () and
236               re.search (r"\\begin *{document}", c.replacement_text())):
237                 modify_preamble (c)
238                 break
239         return chunks
240
241     def snippet_output (self, basename, snippet):
242         str = ''
243         rep = snippet.get_replacements ();
244         rep['base'] = basename
245         str += self.output_print_filename (basename, snippet)
246         if VERBATIM in snippet.option_dict:
247             rep['verb'] = snippet.verb_ly ()
248             str += self.output[VERBATIM] % rep
249
250         str += self.output[OUTPUT] % rep
251
252         ## todo: maintain breaks
253         if 0:
254             breaks = snippet.ly ().count ("\n")
255             str += "".ljust (breaks, "\n").replace ("\n","%\n")
256
257         if QUOTE in snippet.option_dict:
258             rep['str'] = str
259             str = self.output[QUOTE] % rep
260         return str
261
262
263
264
265 BookBase.register_format (BookLatexOutputFormat ());