Upcoming events update
[mp-talk.git] / wiki2beamer-0.9.2
CommitLineData
c8e58f5a
KA
1#!/usr/bin/env python
2
3# wiki2beamer
4#
5# (c) 2007-2008 Michael Rentzsch (http://www.repc.de)
6# (c) 2009-2010 Michael Rentzsch (http://www.repc.de)
7# Kai Dietrich (mail@cleeus.de)
8#
9# Create latex beamer sources for multiple frames from a wiki-like code.
10#
11#
12# This file is part of wiki2beamer.
13# wiki2beamer is free software: you can redistribute it and/or modify
14# it under the terms of the GNU General Public License as published by
15# the Free Software Foundation, either version 2 of the License, or
16# (at your option) any later version.
17#
18# wiki2beamer is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License
24# along with wiki2beamer. If not, see <http://www.gnu.org/licenses/>.
25#
26# Additional commits by:
27# Valentin Haenel <valentin.haenel@gmx.de>
28# Julius Plenz <julius@plenz.com>
29
30
31import sys
32import re
33import random
34import string
35import optparse
36
37VERSIONTAG = "0.9.2"
38__version__= VERSIONTAG
39__author__= "Michael Rentzsch, Kai Dietrich and others"
40
41#python 2.4 compatability
42if sys.version_info >= (2, 5):
43 import hashlib
44else:
45 import md5
46#python 2.4 compatability
47def md5hex(string):
48 if sys.version_info >= (2, 5):
49 return hashlib.md5(string).hexdigest()
50 else:
51 dg = md5.md5()
52 dg.update(string)
53 return dg.hexdigest()
54
55def mydebug(message):
56 """ print debug message to stderr """
57 print >>sys.stderr, message
58
59def syntax_error(message, code):
60 print >>sys.stderr, 'syntax error: %s' % message
61 print >>sys.stderr, '\tcode:\n%s' % code
62 sys.exit(-3)
63
64class IncludeLoopException(Exception):
65 pass
66
67lstbasicstyle=\
68r"""{basic}{
69 captionpos=t,%
70 basicstyle=\footnotesize\ttfamily,%
71 numberstyle=\tiny,%
72 numbers=left,%
73 stepnumber=1,%
74 frame=single,%
75 showspaces=false,%
76 showstringspaces=false,%
77 showtabs=false,%
78 %
79 keywordstyle=\color{blue},%
80 identifierstyle=,%
81 commentstyle=\color{gray},%
82 stringstyle=\color{magenta}%
83}"""
84
85autotemplate = [\
86 ('documentclass', '{beamer}'),\
87 ('usepackage', '{listings}'),\
88 ('usepackage', '{wasysym}'),\
89 ('usepackage', '{graphicx}'),\
90 ('date', '{\\today}'),\
91 ('lstdefinestyle', lstbasicstyle),\
92 ('titleframe', 'True')\
93]
94
95nowikistartre = re.compile(r'^<\[\s*nowiki\s*\]')
96nowikiendre = re.compile(r'^\[\s*nowiki\s*\]>')
97codestartre = re.compile(r'^<\[\s*code\s*\]')
98codeendre = re.compile(r'^\[\s*code\s*\]>')
99
100# lazy initialisation cache for file content
101_file_cache = dict()
102
103def add_lines_to_cache(filename, lines):
104 if not filename in _file_cache:
105 _file_cache[filename] = lines
106 return
107
108
109def get_lines_from_cache(filename):
110 if filename in _file_cache:
111 return _file_cache[filename]
112 else:
113 lines = read_file_to_lines(filename)
114 _file_cache[filename] = lines
115 return lines
116 return
117
118def clear_file_cache():
119 _file_cache = {}
120 return
121
122
123class w2bstate:
124 def __init__(self):
125 self.frame_opened = False
126 self.enum_item_level = ''
127 self.frame_header = ''
128 self.frame_footer = ''
129 self.next_frame_footer = ''
130 self.next_frame_header = ''
131 self.current_line = 0
132 self.autotemplate_opened = False
133 self.defverbs = {}
134 self.code_pos = 0
135 return
136
137 def switch_to_next_frame(self):
138 self.frame_header = self.next_frame_header
139 self.frame_footer = self.next_frame_footer
140 return
141
142def escape_resub(string):
143 p = re.compile(r"\\")
144 return p.sub(r"\\\\", string)
145
146
147def transform_itemenums(string, state):
148 """handle itemizations/enumerations"""
149 preamble = "" # for enumeration/itemize environment commands
150
151 # handle itemizing/enumerations
152 p = re.compile("^([\*\#]+).*$")
153 m = p.match(string)
154 if (m == None):
155 my_enum_item_level = ""
156 else:
157 my_enum_item_level = m.group(1)
158
159 # trivial: old level = new level
160 if (my_enum_item_level == state.enum_item_level):
161 pass
162 else:
163 # find common part
164 common = -1
165 while (len(state.enum_item_level) > common + 1) and \
166 (len(my_enum_item_level) > common + 1) and \
167 (state.enum_item_level[common+1] == my_enum_item_level[common+1]):
168 common = common + 1
169
170 # close enum_item_level environments from back to front
171 for i in range(len(state.enum_item_level)-1, common, -1):
172 if (state.enum_item_level[i] == "*"):
173 preamble = preamble + "\\end{itemize}\n"
174 elif (state.enum_item_level[i] == "#"):
175 preamble = preamble + "\\end{enumerate}\n"
176
177 # open my_enum_item_level environments from front to back
178 for i in range(common+1, len(my_enum_item_level)):
179 if (my_enum_item_level[i] == "*"):
180 preamble = preamble + "\\begin{itemize}\n"
181 elif (my_enum_item_level[i] == "#"):
182 preamble = preamble + "\\begin{enumerate}\n"
183 state.enum_item_level = my_enum_item_level
184
185 # now, substitute item markers
186 p = re.compile("^([\*\#]+)(.*)$")
187 _string = p.sub(r" \\item\2", string)
188 string = preamble + _string
189
190 return string
191
192def transform_define_foothead(string, state):
193 """ header and footer definitions"""
194 p = re.compile("^@FRAMEHEADER=(.*)$", re.VERBOSE)
195 m = p.match(string)
196 if (m != None):
197 #print m.group(1)
198 state.next_frame_header = m.group(1)
199 string = ""
200 p = re.compile("^@FRAMEFOOTER=(.*)$", re.VERBOSE)
201 m = p.match(string)
202 if (m != None):
203 #print m.group(1)
204 state.next_frame_footer = m.group(1)
205 string = ""
206 return string
207
208def transform_detect_manual_frameclose(string, state):
209 """ detect manual closing of frames """
210 p = re.compile(r"\[\s*frame\s*\]>")
211 if state.frame_opened:
212 if p.match(string) != None:
213 state.frame_opened = False
214 return string
215
216def get_frame_closing(state):
217 return " %s \n\\end{frame}\n" % state.frame_footer
218
219def transform_h4_to_frame(string, state):
220 """headings (3) to frames"""
221 frame_opening = r"\\begin{frame}\2\n \\frametitle{\1}\n %s \n" % escape_resub(state.next_frame_header)
222 frame_closing = escape_resub(get_frame_closing(state))
223
224 p = re.compile("^!?====\s*(.*?)\s*====(.*)", re.VERBOSE)
225 if not state.frame_opened:
226 _string = p.sub(frame_opening, string)
227 else:
228 _string = p.sub(frame_closing + frame_opening, string)
229
230 if (string != _string):
231 state.frame_opened = True
232 state.switch_to_next_frame()
233
234 return _string
235
236def transform_h3_to_subsec(string, state):
237 """ headings (2) to subsections """
238 frame_closing = escape_resub(get_frame_closing(state))
239 subsec_opening = r"\n\\subsection\2{\1}\n\n"
240
241 p = re.compile("^===\s*(.*?)\s*===(.*)", re.VERBOSE)
242 if state.frame_opened:
243 _string = p.sub(frame_closing + subsec_opening, string)
244 else:
245 _string = p.sub(subsec_opening, string)
246 if (string != _string):
247 state.frame_opened = False
248
249 return _string
250
251def transform_h2_to_sec(string, state):
252 """ headings (1) to sections """
253 frame_closing = escape_resub(get_frame_closing(state))
254 sec_opening = r"\n\\section\2{\1}\n\n"
255 p = re.compile("^==\s*(.*?)\s*==(.*)", re.VERBOSE)
256 if state.frame_opened:
257 _string = p.sub(frame_closing + sec_opening, string)
258 else:
259 _string = p.sub(sec_opening, string)
260 if (string != _string):
261 state.frame_opened = False
262
263 return _string
264
265def transform_replace_headfoot(string, state):
266 string = string.replace("<---FRAMEHEADER--->", state.frame_header)
267 string = string.replace("<---FRAMEFOOTER--->", state.frame_footer)
268 return string
269
270def transform_environments(string):
271 """
272 latex environments, the users takes full responsibility
273 for closing ALL opened environments
274 exampe:
275 <[block]{block title}
276 message
277 [block]>
278 """
279 # -> open
280 p = re.compile("^<\[([^{}]*?)\]", re.VERBOSE)
281 string = p.sub(r"\\begin{\1}", string)
282 # -> close
283 p = re.compile("^\[([^{}]*?)\]>", re.VERBOSE)
284 string = p.sub(r"\\end{\1}", string)
285
286 return string
287
288def transform_columns(string):
289 """ columns """
290 p = re.compile("^\[\[\[(.*?)\]\]\]", re.VERBOSE)
291 string = p.sub(r"\\column{\1}", string)
292 return string
293
294def transform_boldfont(string):
295 """ bold font """
296 p = re.compile("'''(.*?)'''", re.VERBOSE)
297 string = p.sub(r"\\textbf{\1}", string)
298 return string
299
300def transform_italicfont(string):
301 """ italic font """
302 p = re.compile("''(.*?)''", re.VERBOSE)
303 string = p.sub(r"\\emph{\1}", string)
304 return string
305
306def _transform_mini_parser(character, replacement, string):
307 # implemented as a state-machine
308 output, typewriter = [], []
309 seen_at, seen_escape = False, False
310 for char in string:
311 if seen_escape:
312 if char == character:
313 output.append(character)
314 else:
315 output.append('\\' + char)
316 seen_escape = False
317 elif char == "\\":
318 seen_escape = True
319 elif char == character:
320 if seen_at:
321 seen_at = False
322 output, typewriter = typewriter, output
323 output.append('\\'+replacement+'{')
324 output += typewriter
325 output.append('}')
326 typewriter = []
327 else:
328 seen_at = True
329 output, typewriter = typewriter, output
330 else:
331 output.append(char)
332 if seen_at:
333 output, typewriter = typewriter, output
334 output.append(character)
335 output += typewriter
336 return "".join(output)
337
338def transform_typewriterfont(string):
339 """ typewriter font """
340 return _transform_mini_parser('@', 'texttt', string)
341
342def transform_alerts(string):
343 """ alerts """
344 return _transform_mini_parser('!', 'alert', string)
345
346def transform_colors(string):
347 """ colors """
348 p = re.compile("_([^_\\\\]*?)_([^_]*?[^_\\\\])_", re.VERBOSE)
349 string = p.sub(r"\\textcolor{\1}{\2}", string)
350 return string
351
352def transform_footnotes(string):
353 """ footnotes """
354 p = re.compile("\(\(\((.*?)\)\)\)", re.VERBOSE)
355 string = p.sub(r"\\footnote{\1}", string)
356 return string
357
358def transform_graphics(string):
359 """ figures/images """
360 p = re.compile("\<\<\<(.*?),(.*?)\>\>\>", re.VERBOSE)
361 string = p.sub(r"\\includegraphics[\2]{\1}", string)
362 p = re.compile("\<\<\<(.*?)\>\>\>", re.VERBOSE)
363 string = p.sub(r"\\includegraphics{\1}", string)
364 return string
365
366def transform_substitutions(string):
367 """ substitutions """
368 p = re.compile("(\s)-->(\s)", re.VERBOSE)
369 string = p.sub(r"\1$\\rightarrow$\2", string)
370 p = re.compile("(\s)<--(\s)", re.VERBOSE)
371 string = p.sub(r"\1$\\leftarrow$\2", string)
372 p = re.compile("(\s)==>(\s)", re.VERBOSE)
373 string = p.sub(r"\1$\\Rightarrow$\2", string)
374 p = re.compile("(\s)<==(\s)", re.VERBOSE)
375 string = p.sub(r"\1$\\Leftarrow$\2", string)
376 p = re.compile("(\s):-\)(\s)", re.VERBOSE)
377 string = p.sub(r"\1\\smiley\2", string)
378 p = re.compile("(\s):-\((\s)", re.VERBOSE)
379 string = p.sub(r"\1\\frownie\2", string)
380 return string
381
382def transform_vspace(string):
383 """vspace"""
384 p = re.compile("^\s*--(.*)--\s*$")
385 string = p.sub(r"\n\\vspace{\1}\n", string)
386 return string
387
388def transform_vspacestar(string):
389 """vspace*"""
390 p = re.compile("^\s*--\*(.*)--\s*$")
391 string = p.sub(r"\n\\vspace*{\1}\n", string)
392 return string
393
394def transform_uncover(string):
395 """uncover"""
396 p = re.compile("\+<(.*)>\s*{(.*)") # +<1-2>{.... -> \uncover<1-2>{....
397 string = p.sub(r"\uncover<\1>{\2", string)
398 return string
399
400def transform_only(string):
401 """only"""
402 p = re.compile("-<(.*)>\s*{(.*)") # -<1-2>{.... -> \only<1-2>{....
403 string = p.sub(r"\only<\1>{\2", string)
404 return string
405
406def transform(string, state):
407 """ convert/transform one line in context of state"""
408
409 #string = transform_itemenums(string, state)
410 string = transform_define_foothead(string, state)
411 string = transform_detect_manual_frameclose(string, state)
412 string = transform_h4_to_frame(string, state)
413 string = transform_h3_to_subsec(string, state)
414 string = transform_h2_to_sec(string, state)
415 string = transform_replace_headfoot(string, state)
416
417 string = transform_environments(string)
418 string = transform_columns(string)
419 string = transform_boldfont(string)
420 string = transform_italicfont(string)
421 string = transform_typewriterfont(string)
422 string = transform_alerts(string)
423 string = transform_colors(string)
424 string = transform_footnotes(string)
425 string = transform_graphics(string)
426 string = transform_substitutions(string)
427 string = transform_vspacestar(string)
428 string = transform_vspace(string)
429 string = transform_uncover(string)
430 string = transform_only(string)
431
432 string = transform_itemenums(string, state)
433
434 return string
435
436def expand_code_make_defverb(content, name):
437 return "\\defverbatim[colored]\\%s{\n%s\n}" % (name, content)
438
439def expand_code_make_lstlisting(content, options):
440 return "\\begin{lstlisting}%s%s\\end{lstlisting}" % (options, content)
441
442def expand_code_search_escape_sequences(code):
443 open = '1'
444 close = '2'
445 while code.find(open) != -1 or code.find(close) != -1:
446 open = open + chr(random.randint(48,57))
447 close = close + chr(random.randint(48,57))
448
449 return (open,close)
450
451def expand_code_tokenize_anims(code):
452 #escape
453 (esc_open, esc_close) = expand_code_search_escape_sequences(code)
454 code = code.replace('\\[', esc_open)
455 code = code.replace('\\]', esc_close)
456
457 p = re.compile(r'\[\[(?:.|\s)*?\]\]|\[(?:.|\s)*?\]')
458 non_anim = p.split(code)
459 anim = p.findall(code)
460
461 #unescape
462 anim = map(lambda s: s.replace(esc_open, '\\[').replace(esc_close, '\\]'), anim)
463 non_anim = map(lambda s: s.replace(esc_open, '[').replace(esc_close, ']'), non_anim)
464
465 return (anim, non_anim)
466
467def expand_code_parse_overlayspec(overlayspec):
468 overlays = []
469
470 groups = overlayspec.split(',')
471 for group in groups:
472 group = group.strip()
473 if group.find('-')!=-1:
474 nums = group.split('-')
475 if len(nums)<2:
476 syntax_error('overlay specs must be of the form <(%d-%d)|(%d), ...>', overlayspec)
477 else:
478 try:
479 start = int(nums[0])
480 stop = int(nums[1])
481 except ValueError:
482 syntax_error('not an int, overlay specs must be of the form <(%d-%d)|(%d), ...>', overlayspec)
483
484 overlays.extend(range(start,stop+1))
485 else:
486 try:
487 num = int(group)
488 except ValueError:
489 syntax_error('not an int, overlay specs must be of the form <(%d-%d)|(%d), ...>', overlayspec)
490 overlays.append(num)
491
492 #make unique
493 overlays = list(set(overlays))
494 return overlays
495
496def expand_code_parse_simpleanimspec(animspec):
497 #escape
498 (esc_open, esc_close) = expand_code_search_escape_sequences(animspec)
499 animspec = animspec.replace('\\[', esc_open)
500 animspec = animspec.replace('\\]', esc_close)
501
502 p = re.compile(r'^\[<([0-9,\-]+)>((?:.|\s)*)\]$')
503 m = p.match(animspec)
504 if m != None:
505 overlays = expand_code_parse_overlayspec(m.group(1))
506 code = m.group(2)
507 else:
508 syntax_error('specification does not match [<%d>%s]', animspec)
509
510 #unescape code
511 code = code.replace(esc_open, '[').replace(esc_close, ']')
512
513 return [(overlay, code) for overlay in overlays]
514
515
516def expand_code_parse_animspec(animspec):
517 if len(animspec)<4 or not animspec.startswith('[['):
518 return ('simple', expand_code_parse_simpleanimspec(animspec))
519
520 #escape
521 (esc_open, esc_close) = expand_code_search_escape_sequences(animspec)
522 animspec = animspec.replace('\\[', esc_open)
523 animspec = animspec.replace('\\]', esc_close)
524
525 p = re.compile(r'\[|\]\[|\]')
526 simple_specs = map(lambda s: '[%s]'%s, filter(lambda s: len(s.strip())>0, p.split(animspec)))
527
528 #unescape
529 simple_specs = map(lambda s: s.replace(esc_open, '\\[').replace(esc_close, '\\]'), simple_specs)
530 parsed_simple_specs = map(expand_code_parse_simpleanimspec, simple_specs)
531 #print parsed_simple_specs
532 unified_pss = []
533 for pss in parsed_simple_specs:
534 unified_pss.extend(pss)
535 #print unified_pss
536 return ('double', unified_pss)
537
538
539def expand_code_getmaxoverlay(parsed_anims):
540 max_overlay = 0
541 for anim in parsed_anims:
542 for spec in anim:
543 if spec[0] > max_overlay:
544 max_overlay = spec[0]
545 return max_overlay
546
547def expand_code_getminoverlay(parsed_anims):
548 min_overlay = sys.maxint
549 for anim in parsed_anims:
550 for spec in anim:
551 if spec[0] < min_overlay:
552 min_overlay = spec[0]
553 if min_overlay == sys.maxint:
554 min_overlay = 0
555 return min_overlay
556
557
558def expand_code_genanims(parsed_animspec, minoverlay, maxoverlay, type):
559 #get maximum length of code
560 maxlen=0
561 if type=='double':
562 for simple_animspec in parsed_animspec:
563 if maxlen < len(simple_animspec[1]):
564 maxlen = len(simple_animspec[1])
565
566 out = []
567 fill = ''.join([' ' for i in xrange(0, maxlen)])
568 for x in xrange(minoverlay,maxoverlay+1):
569 out.append(fill[:])
570
571 for simple_animspec in parsed_animspec:
572 out[simple_animspec[0]-minoverlay] = simple_animspec[1]
573
574 return out
575
576def expand_code_getname(code):
577 asciihextable = string.maketrans('0123456789abcdef',\
578 'abcdefghijklmnop')
579 d = md5hex(code).translate(asciihextable)
580 return d
581
582def expand_code_makeoverprint(names, minoverlay):
583 out = ['\\begin{overprint}\n']
584 for (index, name) in enumerate(names):
585 out.append(' \\onslide<%d>\\%s\n' % (index+minoverlay, name))
586 out.append('\\end{overprint}\n')
587
588 return ''.join(out)
589
590def expand_code_get_unique_name(defverbs, code, lstparams):
591 """generate a collision free entry in the defverbs-map and names-list"""
592 name = expand_code_getname(code)
593 expanded_code = expand_code_make_defverb(expand_code_make_lstlisting(code, lstparams), name)
594 rehash = ''
595 while name in defverbs and defverbs[name] != expanded_code:
596 rehash += char(random.randint(65,90)) #append a character from A-Z to rehash value
597 name = expanded_code_getname(code + rehash)
598 expanded_code = expand_code_make_defverb(expand_code_make_lstlisting(code, lstparams), name)
599
600 return (name, expanded_code)
601
602
603def expand_code_segment(result, codebuffer, state):
604 #treat first line as params for lstlistings
605 lstparams = codebuffer[0]
606 codebuffer[0] = ''
607
608 #join lines into one string
609 code = ''.join(codebuffer)
610 #print code
611
612 #tokenize code into anim and non_anim parts
613 (anim, non_anim) = expand_code_tokenize_anims(code)
614 #print anim
615 #print non_anim
616 if len(anim)>0:
617 #generate multiple versions of the anim parts
618 parsed_anims = map(expand_code_parse_animspec, anim)
619 #print parsed_anims
620 max_overlay = expand_code_getmaxoverlay(map(lambda x: x[1], parsed_anims))
621 #if there is unanimated code, use 0 as the starting overlay
622 if len(non_anim)>0:
623 min_overlay = 1
624 else:
625 min_overlay = expand_code_getminoverlay(map(lambda x: x[1], parsed_anims))
626 #print min_overlay
627 #print max_overlay
628 gen_anims = map(lambda x: expand_code_genanims(x[1], min_overlay, max_overlay, x[0]), parsed_anims)
629 #print gen_anims
630 anim_map = {}
631 for i in xrange(0,max_overlay-min_overlay+1):
632 anim_map[i+min_overlay] = map(lambda x: x[i], gen_anims)
633 #print anim_map
634
635 names = []
636 for overlay in sorted(anim_map.keys()):
637 #combine non_anim and anim parts
638 anim_map[overlay].append('')
639 zipped = zip(non_anim, anim_map[overlay])
640 mapped = map(lambda x: x[0] + x[1], zipped)
641 code = ''.join(mapped)
642
643 #generate a collision free entry in the defverbs-map and names-list
644 (name, expanded_code) = expand_code_get_unique_name(state.defverbs, code, lstparams)
645
646 #now we have a collision free entry, append it
647 names.append(name)
648 state.defverbs[name] = expanded_code
649
650 #append overprint area to result
651 overprint = expand_code_makeoverprint(names, min_overlay)
652 result.append(overprint)
653 else:
654 #we have no animations and can just put the defverbatim in
655 #remove escapings
656 code = code.replace('\\[', '[').replace('\\]', ']')
657 (name, expanded_code) = expand_code_get_unique_name(state.defverbs, code, lstparams)
658 state.defverbs[name] = expanded_code
659 result.append('\n\\%s\n' % name)
660
661 #print '----'
662 return
663
664def expand_code_defverbs(result, state):
665 result[state.code_pos] = result[state.code_pos] + '\n'.join(state.defverbs.values()) + '\n'
666 state.defverbs={}
667
668def get_autotemplate_closing():
669 return '\n\end{document}\n'
670
671def parse_bool(string):
672 boolean = False
673
674 if string == 'True' or string == 'true' or string == '1':
675 boolean = True
676 elif string == 'False' or string == 'false' or string =='0':
677 boolean = False
678 else:
679 syntax_error('Boolean expected (True/true/1 or False/false/0)', string)
680
681 return boolean
682
683def parse_autotemplate(autotemplatebuffer):
684 """
685 @param autotemplatebuffer (list)
686 a list of lines found in the autotemplate section
687 @return (list)
688 a list of tuples of the form (string, string) with \command.parameters pairs
689 """
690 autotemplate = []
691
692 for line in autotemplatebuffer:
693 if len(line.lstrip())==0: #ignore empty lines
694 continue
695 if len(line.lstrip())>0 and line.lstrip().startswith('%'): #ignore lines starting with % as comments
696 continue
697
698 tokens = line.split('=', 1)
699 if len(tokens)<2:
700 syntax_error('lines in the autotemplate section have to be of the form key=value', line)
701
702 autotemplate.append((tokens[0], tokens[1]))
703
704 return autotemplate
705
706def parse_usepackage(usepackage):
707 """
708 @param usepackage (str)
709 the unparsed usepackage string in the form [options]{name}
710 @return (tuple)
711 (name(str), options(str))
712 """
713
714 p = re.compile(r'^\s*(\[.*\])?\s*\{(.*)\}\s*$')
715 m = p.match(usepackage)
716 g = m.groups()
717 if len(g)<2 or len(g)>2:
718 syntax_error('usepackage specifications have to be of the form [%s]{%s}', usepackage)
719 elif g[1]==None and g[1].strip()!='':
720 syntax_error('usepackage specifications have to be of the form [%s]{%s}', usepackage)
721 else:
722 options = g[0]
723 name = g[1].strip()
724 return (name, options)
725
726
727def unify_autotemplates(autotemplates):
728 usepackages = {} #packagename : options
729 documentclass = ''
730 titleframe = False
731
732 merged = []
733 for template in autotemplates:
734 for command in template:
735 if command[0] == 'usepackage':
736 (name, options) = parse_usepackage(command[1])
737 usepackages[name] = options
738 elif command[0] == 'titleframe':
739 titleframe = command[1]
740 elif command[0] == 'documentclass':
741 documentclass = command[1]
742 else:
743 merged.append(command)
744
745 autotemplate = []
746 autotemplate.append(('documentclass', documentclass))
747 for (name, options) in usepackages.items():
748 if options != None and options.strip() != '':
749 string = '%s{%s}' % (options, name)
750 else:
751 string = '{%s}' % name
752 autotemplate.append(('usepackage', string))
753 autotemplate.append(('titleframe', titleframe))
754
755 autotemplate.extend(merged)
756
757 return autotemplate
758
759def expand_autotemplate_gen_opening(autotemplate):
760 """
761 @param autotemplate (list)
762 the specification of the autotemplate in the form of a list of tuples
763 @return (string)
764 the string the with generated latex code
765 """
766 titleframe = False
767 out = []
768 for item in autotemplate:
769 if item[0]!='titleframe':
770 out.append('\\%s%s' % item)
771 else:
772 titleframe = parse_bool(item[1])
773
774 out.append('\n\\begin{document}\n')
775 if titleframe:
776 out.append('\n\\frame{\\titlepage}\n')
777
778 return '\n'.join(out)
779
780
781def expand_autotemplate_opening(result, templatebuffer, state):
782 my_autotemplate = parse_autotemplate(templatebuffer)
783 the_autotemplate = unify_autotemplates([autotemplate, my_autotemplate])
784
785 opening = expand_autotemplate_gen_opening(the_autotemplate)
786
787 result.append(opening)
788 result.append('')
789 state.code_pos = len(result)
790 state.autotemplate_opened = True
791 return
792
793def get_autotemplatemode(line, autotemplatemode):
794 autotemplatestart = re.compile(r'^<\[\s*autotemplate\s*\]')
795 autotemplateend = re.compile(r'^\[\s*autotemplate\s*\]>')
796 if not autotemplatemode and autotemplatestart.match(line)!=None:
797 line = autotemplatestart.sub('', line)
798 return (line, True)
799 elif autotemplatemode and autotemplateend.match(line)!=None:
800 line = autotemplateend.sub('', line)
801 return (line, False)
802 else:
803 return (line, autotemplatemode)
804
805def get_nowikimode(line, nowikimode):
806
807 if not nowikimode and nowikistartre.match(line)!=None:
808 line = nowikistartre.sub('', line)
809 return (line, True)
810 elif nowikimode and nowikiendre.match(line)!=None:
811 line = nowikiendre.sub('', line)
812 return (line, False)
813 else:
814 return (line, nowikimode)
815
816def get_codemode(line, codemode):
817 if not codemode and codestartre.match(line)!=None:
818 line = codestartre.sub('', line)
819 return (line, True)
820 elif codemode and codeendre.match(line)!=None:
821 line = codeendre.sub('', line)
822 return (line, False)
823 else:
824 return (line, codemode)
825
826def joinLines(lines):
827 """ join lines ending with unescaped percent signs, unless inside codemode or nowiki mode """
828 nowikimode = False
829 codemode = False
830 r = [] # result array
831 s = '' # new line
832 for _l in lines:
833 (_,nowikimode) = get_nowikimode(_l, nowikimode)
834 if not nowikimode:
835 (_,codemode) = get_codemode(_l, codemode)
836
837 if not codemode:
838 l = _l.rstrip()
839 else:
840 l = _l
841
842 if not (nowikimode or codemode) and (len(l) > 1) and (l[-1] == "%") and (l[-2] != "\\"):
843 s = s + l[:-1]
844 elif not (nowikimode or codemode) and (len(l) == 1) and (l[-1] == "%"):
845 s = s + l[:-1]
846 else:
847 s = s + l
848 r.append(s)
849 s = ''
850
851 return r
852
853def read_file_to_lines(filename):
854 """ read file """
855 try:
856 f = open(filename, "r")
857 lines = joinLines(f.readlines())
858 f.close()
859 except:
860 print >>sys.stdout, "Cannot read file: " + filename
861 sys.exit(-2)
862
863 return lines
864
865
866def scan_for_selected_frames(lines):
867 """scans for frames that should be rendered exclusively, returns true if such frames have been found"""
868 p = re.compile("^!====\s*(.*?)\s*====(.*)", re.VERBOSE)
869 for line in lines:
870 mo = p.match(line)
871 if mo != None:
872 return True
873 return False
874
875def line_opens_unselected_frame(line):
876 p = re.compile("^====\s*(.*?)\s*====(.*)", re.VERBOSE)
877 if p.match(line) != None:
878 return True
879 return False
880
881def line_opens_selected_frame(line):
882 p = re.compile("^!====\s*(.*?)\s*====(.*)", re.VERBOSE)
883 if p.match(line) != None:
884 return True
885 return False
886
887def line_closes_frame(line):
888 p = re.compile("^\s*\[\s*frame\s*\]>", re.VERBOSE)
889 if p.match(line) != None:
890 return True
891 return False
892
893def filter_selected_lines(lines):
894 selected_lines = []
895
896 selected_frame_opened = False
897 unselected_frame_opened = False
898 frame_closed = True
899 frame_manually_closed = False
900 for line in lines:
901 if line_opens_selected_frame(line):
902 selected_frame_opened = True
903 unselected_frame_opened = False
904 frame_closed = False
905
906 if line_opens_unselected_frame(line):
907 unselected_frame_opened = True
908 selected_frame_opened = False
909 frame_closed = False
910
911 if line_closes_frame(line):
912 unselected_frame_opened = False
913 selected_frame_opened = False
914 frame_closed = True
915 frame_manually_closed = True
916
917 if selected_frame_opened or (frame_closed and not frame_manually_closed):
918 selected_lines.append(line)
919
920 return selected_lines
921
922def convert2beamer(lines):
923 out = ""
924 selectedframemode = scan_for_selected_frames(lines)
925 if selectedframemode:
926 out = convert2beamer_selected(lines)
927 else:
928 out = convert2beamer_full(lines)
929
930 return out
931
932def convert2beamer_selected(lines):
933 selected_lines = filter_selected_lines(lines)
934 out = convert2beamer_full(selected_lines)
935 return out
936
937def include_file(line):
938 """ Extract filename to include.
939
940 @param line string
941 a line that might include an inclusion
942 @return string or None
943 if the line contains an inclusion, return the filename,
944 otherwise return None
945 """
946 p = re.compile("\>\>\>(.*?)\<\<\<", re.VERBOSE)
947 if p.match(line):
948 filename = p.sub(r"\1", line)
949 return filename
950 else:
951 return None
952
953def include_file_recursive(base):
954 stack = []
955 output = []
956 def recurse(file_):
957 stack.append(file_)
958 nowikimode = False
959 codemode = False
960 for line in get_lines_from_cache(file_):
961 if nowikimode or codemode:
962 if nowikiendre.match(line):
963 nowikimode = False
964 elif codestartre.match(line):
965 codemode = False
966 output.append(line)
967 elif nowikistartre.match(line):
968 output.append(line)
969 nowikimode = True
970 elif codestartre.match(line):
971 output.append(line)
972 codemode = True
973 else:
974 include = include_file(line)
975 if include is not None:
976 if include in stack:
977 raise IncludeLoopException('Loop detected while trying '
978 "to include: '%s'.\n" % include +
979 'Stack: '+ "->".join(stack))
980 else:
981 recurse(include)
982 else:
983 output.append(line)
984 stack.pop()
985 recurse(base)
986 return output
987
988def convert2beamer_full(lines):
989 """ convert to LaTeX beamer"""
990 state = w2bstate()
991 result = [''] #start with one empty line as line 0
992 codebuffer = []
993 autotemplatebuffer = []
994
995 nowikimode = False
996 codemode = False
997 autotemplatemode = False
998
999 for line in lines:
1000 (line, nowikimode) = get_nowikimode(line, nowikimode)
1001 if nowikimode:
1002 result.append(line)
1003 else:
1004 (line, _codemode) = get_codemode(line, codemode)
1005 if _codemode and not codemode: #code mode was turned on
1006 codebuffer = []
1007 elif not _codemode and codemode: #code mode was turned off
1008 expand_code_segment(result, codebuffer, state)
1009 codemode = _codemode
1010
1011 if codemode:
1012 codebuffer.append(line)
1013 else:
1014 (line, _autotemplatemode) = get_autotemplatemode(line, autotemplatemode)
1015 if _autotemplatemode and not autotemplatemode: #autotemplate mode was turned on
1016 autotemplatebuffer = []
1017 elif not _autotemplatemode and autotemplatemode: #autotemplate mode was turned off
1018 expand_autotemplate_opening(result, autotemplatebuffer, state)
1019 autotemplatemode = _autotemplatemode
1020
1021 if autotemplatemode:
1022 autotemplatebuffer.append(line)
1023 else:
1024 state.current_line = len(result)
1025 result.append(transform(line, state))
1026
1027 result.append(transform("", state)) # close open environments
1028
1029 if state.frame_opened:
1030 result.append(get_frame_closing(state))
1031 if state.autotemplate_opened:
1032 result.append(get_autotemplate_closing())
1033
1034 #insert defverbs somewhere at the beginning
1035 expand_code_defverbs(result, state)
1036
1037 return result
1038
1039def print_result(lines):
1040 """ print result to stdout """
1041 for l in lines:
1042 print >>sys.stdout, l
1043 return
1044
1045def main(argv):
1046 """ check parameters, start file processing """
1047 usage = "%prog wiki2beamer [input1.txt [input2.txt ...]] > output.tex"
1048 version = "%prog (http://wiki2beamer.sf.net), version: " + VERSIONTAG
1049
1050 parser = optparse.OptionParser(usage="\n " + usage, version=version)
1051 opts, args = parser.parse_args()
1052
1053 input_files = []
1054 if not sys.stdin.isatty():
1055 _file_cache['stdin'] = joinLines(sys.stdin.readlines())
1056 input_files.append('stdin')
1057 elif len(args) == 0:
1058 parser.error("You supplied no files to convert!")
1059
1060 input_files += args
1061 lines = []
1062 for file_ in input_files:
1063 try:
1064 lines += include_file_recursive(file_)
1065 except Exception, e:
1066 syntax_error(e, -4)
1067
1068 lines = convert2beamer(lines)
1069 print_result(lines)
1070
1071
1072if (__name__ == "__main__"):
1073 main(sys.argv)
This page took 0.086342 seconds and 4 git commands to generate.