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