Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / python / book_latex.py
blob677e88a1f70fe42a78df7419b8f4f512689c3693
1 # -*- coding: utf-8 -*-
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._
11 progress = ly.progress
12 warning = ly.warning
13 error = ly.error
15 # Recognize special sequences in the input.
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).
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 })''',
37 'lilypond':
38 r'''(?smx)
39 ^[^%\n]*?
40 (?P<match>
41 \\lilypond\s*(
43 \s*(?P<options>.*?)\s*
44 \])?\s*{
45 (?P<code>.*?)
46 })''',
48 'lilypond_block':
49 r'''(?smx)
50 ^[^%\n]*?
51 (?P<match>
52 \\begin\s*(?P<env>{lilypond}\s*)?(
54 \s*(?P<options>.*?)\s*
55 \])?(?(env)|\s*{lilypond})
56 (?P<code>.*?)
57 ^[^%\n]*?
58 \\end\s*{lilypond})''',
60 'lilypond_file':
61 r'''(?smx)
62 ^[^%\n]*?
63 (?P<match>
64 \\lilypondfile\s*(
66 \s*(?P<options>.*?)\s*
67 \])?\s*\{
68 (?P<filename>\S+?)
69 })''',
71 'singleline_comment':
72 r'''(?mx)
73 ^.*?
74 (?P<match>
75 (?P<code>
76 %.*$\n+))''',
78 'verb':
79 r'''(?mx)
80 ^[^%\n]*?
81 (?P<match>
82 (?P<code>
83 \\verb(?P<del>.)
84 .*?
85 (?P=del)))''',
87 'verbatim':
88 r'''(?msx)
89 ^[^%\n]*?
90 (?P<match>
91 (?P<code>
92 \\begin\s*{verbatim}
93 .*?
94 \\end\s*{verbatim}))''',
96 'lilypondversion':
97 r'''(?smx)
98 (?P<match>
99 \\lilypondversion)[^a-zA-Z]''',
102 Latex_output = {
103 FILTER: r'''\begin{lilypond}[%(options)s]
104 %(code)s
105 \end{lilypond}''',
107 OUTPUT: r'''{%%
108 \parindent 0pt
109 \noindent
110 \ifx\preLilyPondExample \undefined
111 \else
112 \expandafter\preLilyPondExample
114 \def\lilypondbook{}%%
115 \input %(base)s-systems.tex
116 \ifx\postLilyPondExample \undefined
117 \else
118 \expandafter\postLilyPondExample
120 }''',
122 PRINTFILENAME: '''\\texttt{%(filename)s}
123 ''',
125 QUOTE: r'''\begin{quotation}
126 %(str)s
127 \end{quotation}''',
129 VERBATIM: r'''\noindent
130 \begin{verbatim}%(verb)s\end{verbatim}
131 ''',
133 VERSION: r'''%(program_version)s''',
141 # Retrieve dimensions from LaTeX
142 LATEX_INSPECTION_DOCUMENT = r'''
143 \nonstopmode
144 %(preamble)s
145 \begin{document}
146 \typeout{textwidth=\the\textwidth}
147 \typeout{columnsep=\the\columnsep}
148 \makeatletter\if@twocolumn\typeout{columns=2}\fi\makeatother
149 \end{document}
152 # Do we need anything else besides `textwidth'?
153 def get_latex_textwidth (source, global_options):
154 m = re.search (r'''(?P<preamble>\\begin\s*{document})''', source)
155 if m == None:
156 warning (_ ("cannot find \\begin{document} in LaTeX document"))
158 ## what's a sensible default?
159 return 550.0
161 preamble = source[:m.start (0)]
162 latex_document = LATEX_INSPECTION_DOCUMENT % {'preamble': preamble}
164 (handle, tmpfile) = tempfile.mkstemp('.tex')
165 logfile = os.path.splitext (tmpfile)[0] + '.log'
166 logfile = os.path.split (logfile)[1]
168 tmp_handle = os.fdopen (handle,'w')
169 tmp_handle.write (latex_document)
170 tmp_handle.close ()
172 ly.system ('%s %s' % (global_options.latex_program, tmpfile),
173 be_verbose=global_options.verbose)
174 parameter_string = file (logfile).read()
176 os.unlink (tmpfile)
177 os.unlink (logfile)
179 columns = 0
180 m = re.search ('columns=([0-9.]+)', parameter_string)
181 if m:
182 columns = int (m.group (1))
184 columnsep = 0
185 m = re.search ('columnsep=([0-9.]+)pt', parameter_string)
186 if m:
187 columnsep = float (m.group (1))
189 textwidth = 0
190 m = re.search ('textwidth=([0-9.]+)pt', parameter_string)
191 if m:
192 textwidth = float (m.group (1))
193 if columns:
194 textwidth = (textwidth - columnsep) / columns
196 return textwidth
199 def modify_preamble (chunk):
200 str = chunk.replacement_text ()
201 if (re.search (r"\\begin *{document}", str)
202 and not re.search ("{graphic[sx]", str)):
203 str = re.sub (r"\\begin{document}",
204 r"\\usepackage{graphics}" + '\n'
205 + r"\\begin{document}",
206 str)
207 chunk.override_text = str
214 class BookLatexOutputFormat (BookBase.BookOutputFormat):
215 def __init__ (self):
216 BookBase.BookOutputFormat.__init__ (self)
217 self.format = "latex"
218 self.default_extension = ".tex"
219 self.snippet_res = Latex_snippet_res
220 self.output = Latex_output
221 self.handled_extensions = ['.latex', '.lytex', '.tex']
222 self.image_formats = "ps"
223 self.snippet_option_separator = '\s*,\s*'
225 def process_options (self, global_options):
226 self.process_options_pdfnotdefault (global_options)
228 def get_line_width (self, source):
229 textwidth = get_latex_textwidth (source, self.global_options)
230 return '%.0f\\pt' % textwidth
232 def input_fullname (self, input_filename):
233 # Use kpsewhich if available, otherwise fall back to the default:
234 if ly.search_exe_path ('kpsewhich'):
235 return os.popen ('kpsewhich ' + input_filename).read()[:-1]
236 else:
237 return BookBase.BookOutputFormat.input_fullname (self, input_filename)
239 def process_chunks (self, chunks):
240 for c in chunks:
241 if (c.is_plain () and
242 re.search (r"\\begin *{document}", c.replacement_text())):
243 modify_preamble (c)
244 break
245 return chunks
247 def snippet_output (self, basename, snippet):
248 str = ''
249 rep = snippet.get_replacements ();
250 rep['base'] = basename
251 str += self.output_print_filename (basename, snippet)
252 if VERBATIM in snippet.option_dict:
253 rep['verb'] = snippet.verb_ly ()
254 str += self.output[VERBATIM] % rep
256 str += self.output[OUTPUT] % rep
258 ## todo: maintain breaks
259 if 0:
260 breaks = snippet.ly ().count ("\n")
261 str += "".ljust (breaks, "\n").replace ("\n","%\n")
263 if QUOTE in snippet.option_dict:
264 str = self.output[QUOTE] % {'str': str}
265 return str
270 BookBase.register_format (BookLatexOutputFormat ());