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