]> git.donarmstrong.com Git - lilypond.git/blob - python/book_latex.py
lilypond-book: Fix printfilename option in latex output.
[lilypond.git] / python / book_latex.py
1 # -*- coding: utf-8 -*-
2
3 import re
4 import tempfile
5 import os
6 import sys
7 import subprocess
8 import book_base as BookBase
9 from book_snippets import *
10 import lilylib as ly
11 global _;_=ly._
12
13 progress = ly.progress
14 warning = ly.warning
15 error = ly.error
16
17 # Recognize special sequences in the input.
18 #
19 #   (?P<name>regex) -- Assign result of REGEX to NAME.
20 #   *? -- Match non-greedily.
21 #   (?!...) -- Match if `...' doesn't match next (without consuming
22 #              the string).
23 #
24 #   (?m) -- Multiline regex: Make ^ and $ match at each line.
25 #   (?s) -- Make the dot match all characters including newline.
26 #   (?x) -- Ignore whitespace in patterns.
27 # Possible keys are:
28 #     'multiline_comment', 'verbatim', 'lilypond_block', 'singleline_comment',
29 #     'lilypond_file', 'include', 'lilypond', 'lilypondversion'
30 Latex_snippet_res = {
31     'include':
32          r'''(?smx)
33           ^[^%\n]*?
34           (?P<match>
35           \\input\s*{
36            (?P<filename>\S+?)
37           })''',
38
39     'lilypond':
40          r'''(?smx)
41           ^[^%\n]*?
42           (?P<match>
43           \\lilypond\s*(
44           \[
45            \s*(?P<options>.*?)\s*
46           \])?\s*{
47            (?P<code>.*?)
48           })''',
49
50     'lilypond_block':
51          r'''(?smx)
52           ^[^%\n]*?
53           (?P<match>
54           \\begin\s*(?P<env>{lilypond}\s*)?(
55           \[
56            \s*(?P<options>.*?)\s*
57           \])?(?(env)|\s*{lilypond})
58            (?P<code>.*?)
59           ^[^%\n]*?
60           \\end\s*{lilypond})''',
61
62     'lilypond_file':
63          r'''(?smx)
64           ^[^%\n]*?
65           (?P<match>
66           \\lilypondfile\s*(
67           \[
68            \s*(?P<options>.*?)\s*
69           \])?\s*\{
70            (?P<filename>\S+?)
71           })''',
72
73     'musicxml_file':
74          r'''(?smx)
75           ^[^%\n]*?
76           (?P<match>
77           \\musicxmlfile\s*(
78           \[
79            \s*(?P<options>.*?)\s*
80           \])?\s*\{
81            (?P<filename>\S+?)
82           })''',
83
84     'singleline_comment':
85          r'''(?mx)
86           ^.*?
87           (?P<match>
88            (?P<code>
89            %.*$\n+))''',
90
91     'verb':
92          r'''(?mx)
93           ^[^%\n]*?
94           (?P<match>
95            (?P<code>
96            \\verb(?P<del>.)
97             .*?
98            (?P=del)))''',
99
100     'verbatim':
101          r'''(?msx)
102           ^[^%\n]*?
103           (?P<match>
104            (?P<code>
105            \\begin\s*{verbatim}
106             .*?
107            \\end\s*{verbatim}))''',
108
109     'lilypondversion':
110          r'''(?smx)
111           (?P<match>
112           \\lilypondversion)[^a-zA-Z]''',
113 }
114
115 Latex_output = {
116     FILTER: r'''\begin{lilypond}[%(options)s]
117 %(code)s
118 \end{lilypond}''',
119
120     OUTPUT: r'''{%%
121 \parindent 0pt
122 \noindent
123 \ifx\preLilyPondExample \undefined
124 \else
125   \expandafter\preLilyPondExample
126 \fi
127 \def\lilypondbook{}%%
128 \input{%(base)s-systems.tex}
129 \ifx\postLilyPondExample \undefined
130 \else
131   \expandafter\postLilyPondExample
132 \fi
133 }''',
134
135     PRINTFILENAME: r'''\texttt{%(filename)s}
136 \linebreak
137 ''',
138
139     QUOTE: r'''\begin{quote}
140 %(str)s
141 \end{quote}''',
142
143     VERBATIM: r'''\noindent
144 \begin{verbatim}%(verb)s\end{verbatim}
145 ''',
146
147     VERSION: r'''%(program_version)s''',
148 }
149
150
151
152
153
154 ###
155 # Retrieve dimensions from LaTeX
156 LATEX_INSPECTION_DOCUMENT = r'''
157 \nonstopmode
158 %(preamble)s
159 \begin{document}
160 \typeout{textwidth=\the\textwidth}
161 \typeout{columnsep=\the\columnsep}
162 \makeatletter\if@twocolumn\typeout{columns=2}\fi\makeatother
163 \end{document}
164 '''
165
166 # Do we need anything else besides `textwidth'?
167 def get_latex_textwidth (source, global_options):
168     m = re.search (r'''(?P<preamble>\\begin\s*{document})''', source)
169     if m == None:
170         warning (_ ("cannot find \\begin{document} in LaTeX document"))
171
172         ## what's a sensible default?
173         return 550.0
174
175     preamble = source[:m.start (0)]
176     latex_document = LATEX_INSPECTION_DOCUMENT % {'preamble': preamble}
177
178     (handle, tmpfile) = tempfile.mkstemp('.tex')
179     tmpfileroot = os.path.splitext (tmpfile)[0]
180     tmpfileroot = os.path.split (tmpfileroot)[1]
181     auxfile = tmpfileroot + '.aux'
182     logfile = tmpfileroot + '.log'
183
184     tmp_handle = os.fdopen (handle,'w')
185     tmp_handle.write (latex_document)
186     tmp_handle.close ()
187
188     progress (_ ("Running `%s' on file `%s' to detect default page settings.\n")
189               % (global_options.latex_program, tmpfile));
190     cmd = '%s %s' % (global_options.latex_program, tmpfile);
191     ly.debug_output ("Executing: %s\n" % cmd);
192     run_env = os.environ.copy()
193     run_env['LC_ALL'] = 'C'
194
195     ### unknown why this is necessary
196     universal_newlines = True
197     if sys.platform == 'mingw32':
198         universal_newlines = False
199         ### use os.system to avoid weird sleep() problems on
200         ### GUB's python 2.4.2 on mingw
201         # make file to write to
202         output_dir = tempfile.mkdtemp()
203         output_filename = os.path.join(output_dir, 'output.txt')
204         # call command
205         cmd += " > %s" % output_filename
206         returncode = os.system(cmd)
207         parameter_string = open(output_filename).read()
208         if returncode != 0:
209             warning (_ ("Unable to auto-detect default settings:\n"))
210         # clean up
211         os.remove(output_filename)
212         os.rmdir(output_dir)
213     else:
214         proc = subprocess.Popen (cmd,
215             env=run_env,
216             universal_newlines=universal_newlines,
217             shell=True,
218             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
219         (parameter_string, error_string) = proc.communicate ()
220         if proc.returncode != 0:
221             warning (_ ("Unable to auto-detect default settings:\n%s")
222                     % error_string)
223     os.unlink (tmpfile)
224     if os.path.exists (auxfile):
225         os.unlink (auxfile)
226     if os.path.exists (logfile):
227         parameter_string = file (logfile).read()
228         os.unlink (logfile)
229
230     columns = 0
231     m = re.search ('columns=([0-9.]+)', parameter_string)
232     if m:
233         columns = int (m.group (1))
234
235     columnsep = 0
236     m = re.search ('columnsep=([0-9.]+)pt', parameter_string)
237     if m:
238         columnsep = float (m.group (1))
239
240     textwidth = 0
241     m = re.search ('textwidth=([0-9.]+)pt', parameter_string)
242     if m:
243         textwidth = float (m.group (1))
244         if columns:
245             textwidth = (textwidth - columnsep) / columns
246
247     return textwidth
248
249
250 def modify_preamble (chunk):
251     str = chunk.replacement_text ()
252     if (re.search (r"\\begin *{document}", str)
253       and not re.search ("{graphic[sx]", str)):
254         str = re.sub (r"\\begin{document}",
255                r"\\usepackage{graphics}" + '\n'
256                + r"\\begin{document}",
257                str)
258         chunk.override_text = str
259
260
261
262
263
264
265 class BookLatexOutputFormat (BookBase.BookOutputFormat):
266     def __init__ (self):
267         BookBase.BookOutputFormat.__init__ (self)
268         self.format = "latex"
269         self.default_extension = ".tex"
270         self.snippet_res = Latex_snippet_res
271         self.output = Latex_output
272         self.handled_extensions = ['.latex', '.lytex', '.tex']
273         self.image_formats = "ps"
274         self.snippet_option_separator = '\s*,\s*'
275
276     def process_options (self, global_options):
277         self.process_options_pdfnotdefault (global_options)
278
279     def get_line_width (self, source):
280         textwidth = get_latex_textwidth (source, self.global_options)
281         return '%.0f\\pt' % textwidth
282
283     def input_fullname (self, input_filename):
284         # Use kpsewhich if available, otherwise fall back to the default:
285         if ly.search_exe_path ('kpsewhich'):
286             return os.popen ('kpsewhich ' + input_filename).read()[:-1]
287         else:
288             return BookBase.BookOutputFormat.input_fullname (self, input_filename)
289
290     def process_chunks (self, chunks):
291         for c in chunks:
292             if (c.is_plain () and
293               re.search (r"\\begin *{document}", c.replacement_text())):
294                 modify_preamble (c)
295                 break
296         return chunks
297
298     def snippet_output (self, basename, snippet):
299         str = ''
300         rep = snippet.get_replacements ();
301         rep['base'] = basename.replace ('\\', '/')
302         rep['filename'] = os.path.basename (snippet.filename).replace ('\\', '/')
303         rep['ext'] = snippet.ext
304         if PRINTFILENAME in snippet.option_dict:
305             str += self.output[PRINTFILENAME] % rep
306         if VERBATIM in snippet.option_dict:
307             rep['verb'] = snippet.verb_ly ()
308             str += self.output[VERBATIM] % rep
309
310         str += self.output[OUTPUT] % rep
311
312         ## todo: maintain breaks
313         if 0:
314             breaks = snippet.ly ().count ("\n")
315             str += "".ljust (breaks, "\n").replace ("\n","%\n")
316
317         if QUOTE in snippet.option_dict:
318             str = self.output[QUOTE] % {'str': str}
319         return str
320
321
322
323
324 BookBase.register_format (BookLatexOutputFormat ());