class Slide(object): """ Slide object. """ @classmethod def reset(cls): """Reset to default state.""" return def __init__(self, number, position=None, title=None, contents=None): """" Paramters --------- number: int slide global numeration position: dict position dictionary containing {'x': posx, 'y': posy, 'z': posz, 'rotx': rotx, 'roty': roty, 'rotz': rotz, 'scale': scaling} title: str contents: str """ self.number = number self.position = None self.set_position(position) self.title = title self.contents = contents self.overtheme = Theme() return def __str__(self): strings = [str(self.title)] strings.append(str(self.contents)) return ''.join(strings) def get_overtheme(self, parser): """Get eventaul overtheme definition. Parameters ---------- parser: Parser """ codeblocks = parser.tokenizer(source=self.contents, re_search=parser.regexs['codeblock']) yamlblocks = parser.tokenizer(source=self.contents, re_search=parser.regexs['yamlblock'], exclude=codeblocks) if len(yamlblocks) > 0: self.overtheme.get(source=''.join([block['match'].group().strip('---') for block in yamlblocks]), name='overtheme', div_id='slide-' + str(self.number)) purged_contents = self.contents[:yamlblocks[0]['start']] for b, yamlblock in enumerate(yamlblocks[:-1]): purged_contents += self.contents[yamlblock['end']:yamlblocks[b + 1]['start']] purged_contents += self.contents[yamlblocks[-1]['end']:] self.contents = purged_contents def set_position(self, position): """Set slide position. Parameters ---------- position: dict position dictionary containing {'x': posx, 'y': posy, 'z': posz, 'rotx': rotx, 'roty': roty, 'rotz': rotz, 'scale': scaling} """ if position is not None: self.position = {} for key in position: self.position[key] = position[key] def put_html_attributes(self, doc): """Put html attibutes of the slide. Parameters ---------- doc: Doc """ doc.attr(('id', 'slide-' + str(self.number))) # doc.attr(('title', str(self.title))) doc.attr(('class', 'step slide')) doc.attr(('data-x', str(self.position['x']))) doc.attr(('data-y', str(self.position['y']))) doc.attr(('data-z', str(self.position['z']))) doc.attr(('data-scale', str(self.position['scale']))) doc.attr(('data-rotate-x', str(self.position['rotx']))) doc.attr(('data-rotate-y', str(self.position['roty']))) doc.attr(('data-rotate-z', str(self.position['rotz']))) return def to_html(self, doc, parser, metadata, theme, current): """Generate html from self. Parameters ---------- doc: Doc parser: Parser metatadata: dict presentation metadata theme: Theme() presentation theme current: list """ def _parse_env(Env, re_search, source): codeblocks = parser.tokenizer(source=source, re_search=parser.regexs['codeblock']) codes = parser.tokenizer(source=source, re_search=parser.regexs['code'], exclude=codeblocks) yamlblocks = parser.tokenizer(source=source, re_search=parser.regexs['yamlblock'], exclude=codeblocks + codes) envs = parser.tokenizer(source=source, re_search=re_search, exclude=codeblocks + yamlblocks + codes) if len(envs) > 0: parsed_source = source[:envs[0]['start']] for e, env in enumerate(envs[:-1]): current = Env(source=env['match'].group()) parsed_source += current.to_html() + source[env['end']:envs[e + 1]['start']] if Env is Video: if self.overtheme.custom: current = Env(source=envs[-1]['match'].group(), theme=self.overtheme) else: current = Env(source=envs[-1]['match'].group(), theme=theme) else: current = Env(source=envs[-1]['match'].group()) parsed_source += current.to_html() + source[envs[-1]['end']:] return parsed_source return source html = self.contents for meta in metadata: html = metadata[meta].parse(parser=parser, source=html, toc_depth=metadata['toc_depth'].value, max_time=metadata['max_time'].value, current=current) html = _parse_env(Env=Box, re_search=Box.regexs['box'], source=html) html = _parse_env(Env=Note, re_search=Note.regexs['note'], source=html) html = _parse_env(Env=Figure, re_search=Figure.regexs['figure'], source=html) html = _parse_env(Env=Table, re_search=Table.regexs['table'], source=html) html = _parse_env(Env=Video, re_search=Video.regexs['video'], source=html) html = _parse_env(Env=Columns, re_search=Columns.regexs['columns'], source=html) with doc.tag('div', klass='slide-content'): doc.asis(markdown2html(source=html)) return
class Presentation(object): """ Presentation object. Attributes ---------- chapters_number: int """ chapters_number = 0 @classmethod def reset(cls): """Reset to default state.""" cls.chapters_number = 0 Theme.reset() Chapter.reset() def __init__(self): """ Attributes ---------- metadata: dict presentation metadata; each element of the dictionary if a dict with ['value', 'user'] items: value contains the metadata value and user indicates if the value comes from user (if True) or from defaults (if False). """ self.reset() self.metadata = {'title': Metadata(name='title', value=''), 'subtitle': Metadata(name='subtitle', value=''), 'authors': Metadata(name='authors', value=[]), 'authors_short': Metadata(name='authors_short', value=[]), 'emails': Metadata(name='emails', value=[]), 'affiliations': Metadata(name='affiliations', value=[]), 'affiliations_short': Metadata(name='affiliations_short', value=[]), 'logo': Metadata(name='logo', value=''), 'timer': Metadata(name='timer', value=''), 'location': Metadata(name='location', value=''), 'location_short': Metadata(name='location_short', value=''), 'date': Metadata(name='date', value=''), 'conference': Metadata(name='conference', value=''), 'conference_short': Metadata(name='conference_short', value=''), 'session': Metadata(name='session', value=''), 'session_short': Metadata(name='session_short', value=''), 'max_time': Metadata(name='max_time', value='25'), 'total_slides_number': Metadata(name='total_slides_number', value=''), 'dirs_to_copy': Metadata(name='dirs_to_copy', value=[]), 'toc': Metadata(name='toc', value=OrderedDict()), 'toc_depth': Metadata(name='toc_depth', value='2'), 'chaptertitle': Metadata(name='chaptertitle', value=''), 'chapternumber': Metadata(name='chapternumber', value=''), 'sectiontitle': Metadata(name='sectiontitle', value=''), 'sectionnumber': Metadata(name='sectionnumber', value=''), 'subsectiontitle': Metadata(name='subsectiontitle', value=''), 'subsectionnumber': Metadata(name='subsectionnumber', value=''), 'slidetitle': Metadata(name='slidetitle', value=''), 'slidenumber': Metadata(name='slidenumber', value=''), 'css_overtheme': Metadata(name='css_overtheme', value=[]), 'custom': Metadata(name='custom-[0-9]*', value='')} self.theme = Theme() self.parser = Parser() self.chapters = [] self.position = Position() return def __str__(self): strings = ['Chapters number ' + str(Presentation.chapters_number)] strings.append('Sections number ' + str(Chapter.sections_number)) strings.append('Subsections number ' + str(Section.subsections_number)) strings.append('Slides number ' + str(Subsection.slides_number)) for chapter in self.chapters: strings.append(str(chapter)) return '\n'.join(strings) def __update_toc(self): """Update TOC after a new chapter (the last one) has been added.""" self.metadata['toc'].value[self.chapters[-1].title] = self.chapters[-1].toc def __get_metadata(self, source): """ Get metadata from source stream. Parameters ---------- source: str """ codeblocks = self.parser.tokenizer(source=source, re_search=self.parser.regexs['codeblock']) yamlblocks = self.parser.tokenizer(source=source, re_search=self.parser.regexs['yamlblock'], exclude=codeblocks) try: for block in yamlblocks: for data in load_all(block['match'].group().strip('---')): if 'metadata' in data: for element in data['metadata']: for key in element: if key in self.metadata: self.metadata[key].update_value(value=element[key]) except YAMLError: print('No valid definition of metadata has been found') def __get_theme(self, source): """ Get theme from source stream. Parameters ---------- source: str """ codeblocks = self.parser.tokenizer(source=source, re_search=self.parser.regexs['codeblock']) yamlblocks = self.parser.tokenizer(source=source, re_search=self.parser.regexs['yamlblock'], exclude=codeblocks) self.theme.get(''.join([block['match'].group().strip('---') for block in yamlblocks])) def __add_chapter(self, chapter): """ Add a chapter to the pesentation. Parameters ---------- chapter: Chapter """ Presentation.chapters_number += 1 self.chapters.append(chapter) self.__update_toc() return def __check_bad_sectioning(self, tokens): """Check if the presentation has a bad sectioning. Parameters ---------- tokens: Parser.tokens source: str """ if '$titlepage' not in tokens['slides'][0]['match'].group().lower(): if tokens['slides'][0]['start'] < tokens['subsections'][0]['start'] or tokens['slides'][0]['start'] < tokens['sections'][0]['start'] or tokens['slides'][0]['start'] < tokens['chapters'][0]['start']: print('Warning: found bad presentation sectioning!') print('The slide definition:') print(tokens['slides'][0]['match'].group() + "\n") print('is placed before the first defined chapter/section/subsection.') print('All contents before the first defined chapter/section/subsection is omitted!') print() return def __put_html_tag_head(self, doc, tag, text, config): """Put head tag into html doc. Parameters ---------- doc: Doc() tag: Tag() config : MatisseConfig MaTiSSe configuration """ with tag('head'): doc.stag('meta', charset='utf-8') doc.stag('meta', author=' and '.join(self.metadata['authors'].value)) with tag('title'): text(self.metadata['title'].value) doc.stag('meta', subtitle=self.metadata['subtitle'].value) doc.stag('link', rel='stylesheet', href='css/normalize.css') doc.stag('link', rel='stylesheet', href='css/matisse_defaults.css') doc.stag('link', rel='stylesheet', href='css/matisse_defaults_printing.css') if config.highlight: doc.stag('link', rel='stylesheet', href='js/highlight/styles/' + config.highlight_style) doc.stag('link', rel='stylesheet', href='css/theme.css') for css in self.metadata['css_overtheme'].value: doc.stag('link', rel='stylesheet', href=css) for chapter in self.chapters: for section in chapter.sections: for subsection in section.subsections: for slide in subsection.slides: if slide.overtheme.custom: doc.stag('link', rel='stylesheet', href='css/slide-' + str(slide.number) + '-overtheme.css') def __put_html_tags_scripts(self, doc, tag, config): """Put final tags for scripts into html doc. Parameters ---------- doc: Doc() tag: Tag() config : MatisseConfig MaTiSSe configuration """ with tag('script'): doc.attr(src='js/countDown.js') with tag('script'): doc.attr(src='js/impress.js') if not config.pdf: with tag('script'): doc.asis('impress().init();') if config.online_mathjax: with tag('script'): doc.attr(('type', 'text/javascript')) doc.attr(src='http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML') else: with tag('script'): doc.attr(('type', 'text/x-mathjax-config')) doc.text(""" MathJax.Hub.Config({ extensions: ["tex2jax.js"], jax: ["input/TeX", "output/HTML-CSS"], tex2jax: { inlineMath: [ ['$','$'] ], displayMath: [ ['$$','$$'] ], processEscapes: true }, "HTML-CSS": { availableFonts: ["Neo-Euler"] } }); """) with tag('script'): doc.attr(('type', 'text/javascript')) doc.attr(src='js/MathJax/MathJax.js') if config.highlight: with tag('script'): doc.attr(src='js/highlight/highlight.pack.js') with tag('script'): doc.text("""hljs.initHighlightingOnLoad();""") def __put_html_slide_decorators(self, tag, doc, decorator, position=None, overtheme=None, current=None): """Put html data of headers, footers and sidebars. Parameters ---------- doc: Doc tag: tag decorator: {header, footer, sidebar} position: {'L','R'} sidebars position, L => left, R => right current: list """ if overtheme is not None and overtheme.custom: theme = overtheme else: theme = self.theme # decorators = getattr(self.theme, 'slide_' + decorator) decorators = getattr(theme, 'slide_' + decorator) for decor in sorted(decorators): insert = True # position check for sidebars if decorator == 'sidebar' and position is not None: for css in decorators[decor]: for key in css: if 'position' in key.lower(): pos = css[key] break insert = pos.lower() == position.lower() # active check for css in decorators[decor]: for key in css: if 'active' in key.lower(): insert = insert and css[key].lower() == 'yes' if insert: # placeholders = self.theme.get_slide_decorators_metadata(decorator=decorator, name=decor) placeholders = theme.get_slide_decorators_metadata(decorator=decorator, name=decor) for metadata in self.metadata: placeholders = self.metadata[metadata].parse(parser=self.parser, source=placeholders, toc_depth=self.metadata['toc_depth'].value, max_time=self.metadata['max_time'].value, current=current) with tag('div', klass='slide-' + decor): doc.asis(placeholders) def parse(self, config, source): """Parse presentation from source stream. Parameters ---------- config : MatisseConfig MaTiSSe configuration source: str """ complete_source = self.parser.includes(source=source) self.__get_metadata(source=complete_source) self.__get_theme(source=complete_source) new_theme = Theme() new_theme.set_from(other=self.theme) tokens = self.parser.tokenize(source=complete_source) self.__check_bad_sectioning(tokens=tokens) chapters_number = 0 sections_number = 0 subsections_number = 0 slides_number = 0 titlepage_inserted = False for chap in tokens['chapters']: chapters_number += 1 slide_local_numbers = [0, 0, 0] if chap['match'].group('expr'): chapter = Chapter(number=chapters_number, title=chap['match'].group('expr')) else: chapter = Chapter(number=chapters_number, title='') for sec in tokens['sections']: if sec['start'] >= chap['start'] and sec['start'] <= chap['end_next']: sections_number += 1 slide_local_numbers[1] = 0 slide_local_numbers[2] = 0 section = Section(number=sections_number, title=sec['match'].group('expr')) for subsec in tokens['subsections']: if subsec['start'] >= sec['start'] and subsec['start'] <= sec['end_next']: subsections_number += 1 slide_local_numbers[2] = 0 subsection = Subsection(number=subsections_number, title=subsec['match'].group('expr')) for sld in tokens['slides']: if '$titlepage' in sld['match'].group().lower() and not titlepage_inserted: slide = Slide(number=0, title='titlepage', contents=complete_source[sld['end']:sld['end_next']]) slide.get_overtheme(parser=self.parser) if slide.overtheme.copy_from_theme is not None and slide.overtheme.copy_from_theme: slide.overtheme.copy_from(other=self.theme) self.position.update_position(presentation_theme=self.theme, overtheme=slide.overtheme) slide.set_position(position=self.position.position) subsection.add_slide(slide=slide) titlepage_inserted = True else: if sld['start'] >= subsec['start'] and sld['start'] <= subsec['end_next']: slide_local_numbers[0] += 1 slide_local_numbers[1] += 1 slide_local_numbers[2] += 1 if slide_local_numbers[0] == 1 and config.toc_at_chap_beginning is not None: slides_number += 1 self.position.update_position(presentation_theme=self.theme) subsection.add_slide(slide=Slide(number=slides_number, position=self.position.position, title='Table of Contents', contents='$toc[depth:' + str(config.toc_at_chap_beginning) + ']')) if slide_local_numbers[1] == 1 and config.toc_at_sec_beginning is not None: slides_number += 1 self.position.update_position(presentation_theme=self.theme) subsection.add_slide(slide=Slide(number=slides_number, position=self.position.position, title='Table of Contents', contents='$toc[depth:' + str(config.toc_at_sec_beginning) + ']')) if slide_local_numbers[2] == 1 and config.toc_at_subsec_beginning is not None: slides_number += 1 self.position.update_position(presentation_theme=self.theme) subsection.add_slide(slide=Slide(number=slides_number, position=self.position.position, title='Table of Contents', contents='$toc[depth:' + str(config.toc_at_subsec_beginning) + ']')) slides_number += 1 slide = Slide(number=slides_number, title=sld['match'].group('expr'), contents=complete_source[sld['end']:sld['end_next']]) slide.get_overtheme(parser=self.parser) if slide.overtheme.copy_from_theme is not None and slide.overtheme.copy_from_theme: slide.overtheme.copy_from(other=self.theme) self.position.update_position(presentation_theme=self.theme, overtheme=slide.overtheme) slide.set_position(position=self.position.position) subsection.add_slide(slide=slide) section.add_subsection(subsection=subsection) chapter.add_section(section=section) self.__add_chapter(chapter=chapter) self.metadata['total_slides_number'].update_value(value=str(Subsection.slides_number)) def to_html(self, config): """Generate a html stream of the whole presentation. Parameters ---------- config : MatisseConfig MaTiSSe configuration """ doc, tag, text = Doc().tagtext() doc.asis('<!DOCTYPE html>') with tag('html'): # doc.attr(title=self.metadata['title'].value) self.__put_html_tag_head(doc=doc, tag=tag, text=text, config=config) with tag('body', onload="resetCountdown(" + str(self.metadata['max_time'].value) + ");"): doc.attr(klass='impress-not-supported') with tag('div', id='impress'): # numbering: [local_chap, local_sec, local_subsec, local_slide] current = [0, 0, 0, 0] for chapter in self.chapters: current[0] += 1 current[1] = 0 current[2] = 0 current[3] = 0 self.metadata['chaptertitle'].update_value(value=chapter.title) self.metadata['chapternumber'].update_value(value=chapter.number) for section in chapter.sections: current[1] += 1 current[2] = 0 current[3] = 0 self.metadata['sectiontitle'].update_value(value=section.title) self.metadata['sectionnumber'].update_value(value=section.number) for subsection in section.subsections: current[2] += 1 current[3] = 0 self.metadata['subsectiontitle'].update_value(value=subsection.title) self.metadata['subsectionnumber'].update_value(value=subsection.number) for slide in subsection.slides: current[3] += 1 self.metadata['slidetitle'].update_value(value=slide.title) self.metadata['slidenumber'].update_value(value=slide.number) with doc.tag('div'): chapter.put_html_attributes(doc=doc) section.put_html_attributes(doc=doc) subsection.put_html_attributes(doc=doc) slide.put_html_attributes(doc=doc) self.__put_html_slide_decorators(tag=tag, doc=doc, decorator='header', current=current, overtheme=slide.overtheme) self.__put_html_slide_decorators(tag=tag, doc=doc, decorator='sidebar', position='L', current=current, overtheme=slide.overtheme) slide.to_html(doc=doc, parser=self.parser, metadata=self.metadata, theme=self.theme, current=current) self.__put_html_slide_decorators(tag=tag, doc=doc, decorator='sidebar', position='R', current=current, overtheme=slide.overtheme) self.__put_html_slide_decorators(tag=tag, doc=doc, decorator='footer', current=current, overtheme=slide.overtheme) self.__put_html_tags_scripts(doc=doc, tag=tag, config=config) # source = re.sub(r"<li>(?P<item>.*)</li>", r"<li><span>\g<item></span></li>", source) html = indent(doc.getvalue()) return html def save(self, config, output): """Save the html form of presentation into external file. Parameters ---------- config : MatisseConfig MaTiSSe configuration output : str output path """ if not os.path.exists(output): os.makedirs(output) with open(os.path.join(output, 'index.html'), 'w') as html: html.write(self.to_html(config=config)) # copy user defined directories if set if len(self.metadata['dirs_to_copy'].value) > 0: for data in self.metadata['dirs_to_copy'].value: sync_logger = logging.getLogger('sync_logger') sync(data, os.path.join(output, data), 'sync', create=True, logger=sync_logger) # css files with open(os.path.join(output, 'css/theme.css'), 'w') as css_theme: css_theme.writelines(self.theme.css) for chapter in self.chapters: for section in chapter.sections: for subsection in section.subsections: for slide in subsection.slides: if slide.overtheme.custom: with open(os.path.join(output, 'css/slide-' + str(slide.number) + '-overtheme.css'), 'w') as css_theme: css_theme.writelines(slide.overtheme.css) return
class Presentation(object): """ Presentation object. Attributes ---------- chapters_number: int """ chapters_number = 0 @classmethod def reset(cls): """Reset to default state.""" cls.chapters_number = 0 Theme.reset() Chapter.reset() def __init__(self): """ Attributes ---------- metadata: dict presentation metadata; each element of the dictionary if a dict with ['value', 'user'] items: value contains the metadata value and user indicates if the value comes from user (if True) or from defaults (if False). """ self.reset() self.metadata = { 'title': Metadata(name='title', value=''), 'subtitle': Metadata(name='subtitle', value=''), 'authors': Metadata(name='authors', value=[]), 'authors_short': Metadata(name='authors_short', value=[]), 'emails': Metadata(name='emails', value=[]), 'affiliations': Metadata(name='affiliations', value=[]), 'affiliations_short': Metadata(name='affiliations_short', value=[]), 'logo': Metadata(name='logo', value=''), 'timer': Metadata(name='timer', value=''), 'location': Metadata(name='location', value=''), 'location_short': Metadata(name='location_short', value=''), 'date': Metadata(name='date', value=''), 'conference': Metadata(name='conference', value=''), 'conference_short': Metadata(name='conference_short', value=''), 'session': Metadata(name='session', value=''), 'session_short': Metadata(name='session_short', value=''), 'max_time': Metadata(name='max_time', value='25'), 'total_slides_number': Metadata(name='total_slides_number', value=''), 'dirs_to_copy': Metadata(name='dirs_to_copy', value=[]), 'toc': Metadata(name='toc', value=OrderedDict()), 'toc_depth': Metadata(name='toc_depth', value='2'), 'chaptertitle': Metadata(name='chaptertitle', value=''), 'chapternumber': Metadata(name='chapternumber', value=''), 'sectiontitle': Metadata(name='sectiontitle', value=''), 'sectionnumber': Metadata(name='sectionnumber', value=''), 'subsectiontitle': Metadata(name='subsectiontitle', value=''), 'subsectionnumber': Metadata(name='subsectionnumber', value=''), 'slidetitle': Metadata(name='slidetitle', value=''), 'slidenumber': Metadata(name='slidenumber', value=''), 'css_overtheme': Metadata(name='css_overtheme', value=[]), 'custom': Metadata(name='custom-[0-9]*', value='') } self.theme = Theme() self.parser = Parser() self.chapters = [] self.position = Position() return def __str__(self): strings = ['Chapters number ' + str(Presentation.chapters_number)] strings.append('Sections number ' + str(Chapter.sections_number)) strings.append('Subsections number ' + str(Section.subsections_number)) strings.append('Slides number ' + str(Subsection.slides_number)) for chapter in self.chapters: strings.append(str(chapter)) return '\n'.join(strings) def __update_toc(self): """Update TOC after a new chapter (the last one) has been added.""" self.metadata['toc'].value[ self.chapters[-1].title] = self.chapters[-1].toc def __get_metadata(self, source): """ Get metadata from source stream. Parameters ---------- source: str """ codeblocks = self.parser.tokenizer( source=source, re_search=self.parser.regexs['codeblock']) yamlblocks = self.parser.tokenizer( source=source, re_search=self.parser.regexs['yamlblock'], exclude=codeblocks) try: for block in yamlblocks: for data in load_all(block['match'].group().strip('---')): if 'metadata' in data: for element in data['metadata']: for key in element: if key in self.metadata: self.metadata[key].update_value( value=element[key]) except YAMLError: print('No valid definition of metadata has been found') def __get_theme(self, source): """ Get theme from source stream. Parameters ---------- source: str """ codeblocks = self.parser.tokenizer( source=source, re_search=self.parser.regexs['codeblock']) yamlblocks = self.parser.tokenizer( source=source, re_search=self.parser.regexs['yamlblock'], exclude=codeblocks) self.theme.get(''.join( [block['match'].group().strip('---') for block in yamlblocks])) def __add_chapter(self, chapter): """ Add a chapter to the pesentation. Parameters ---------- chapter: Chapter """ Presentation.chapters_number += 1 self.chapters.append(chapter) self.__update_toc() return def __check_bad_sectioning(self, tokens): """Check if the presentation has a bad sectioning. Parameters ---------- tokens: Parser.tokens source: str """ if '$titlepage' not in tokens['slides'][0]['match'].group().lower(): if tokens['slides'][0]['start'] < tokens['subsections'][0][ 'start'] or tokens['slides'][0]['start'] < tokens[ 'sections'][0]['start'] or tokens['slides'][0][ 'start'] < tokens['chapters'][0]['start']: print('Warning: found bad presentation sectioning!') print('The slide definition:') print(tokens['slides'][0]['match'].group() + "\n") print( 'is placed before the first defined chapter/section/subsection.' ) print( 'All contents before the first defined chapter/section/subsection is omitted!' ) print() return def __put_html_tag_head(self, doc, tag, text, config): """Put head tag into html doc. Parameters ---------- doc: Doc() tag: Tag() config : MatisseConfig MaTiSSe configuration """ with tag('head'): doc.stag('meta', charset='utf-8') doc.stag('meta', author=' and '.join(self.metadata['authors'].value)) with tag('title'): text(self.metadata['title'].value) doc.stag('meta', subtitle=self.metadata['subtitle'].value) doc.stag('link', rel='stylesheet', href='css/normalize.css') doc.stag('link', rel='stylesheet', href='css/matisse_defaults.css') doc.stag('link', rel='stylesheet', href='css/matisse_defaults_printing.css') if config.highlight: doc.stag('link', rel='stylesheet', href='js/highlight/styles/' + config.highlight_style) doc.stag('link', rel='stylesheet', href='css/theme.css') for css in self.metadata['css_overtheme'].value: doc.stag('link', rel='stylesheet', href=css) for chapter in self.chapters: for section in chapter.sections: for subsection in section.subsections: for slide in subsection.slides: if slide.overtheme.custom: doc.stag('link', rel='stylesheet', href='css/slide-' + str(slide.number) + '-overtheme.css') def __put_html_tags_scripts(self, doc, tag, config): """Put final tags for scripts into html doc. Parameters ---------- doc: Doc() tag: Tag() config : MatisseConfig MaTiSSe configuration """ with tag('script'): doc.attr(src='js/countDown.js') with tag('script'): doc.attr(src='js/impress.js') if not config.pdf: with tag('script'): doc.asis('impress().init();') if config.online_mathjax: with tag('script'): doc.attr(('type', 'text/javascript')) doc.attr( src= 'http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML' ) else: with tag('script'): doc.attr(('type', 'text/x-mathjax-config')) doc.text(""" MathJax.Hub.Config({ extensions: ["tex2jax.js"], jax: ["input/TeX", "output/HTML-CSS"], tex2jax: { inlineMath: [ ['$','$'] ], displayMath: [ ['$$','$$'] ], processEscapes: true }, "HTML-CSS": { availableFonts: ["Neo-Euler"] } }); """) with tag('script'): doc.attr(('type', 'text/javascript')) doc.attr(src='js/MathJax/MathJax.js') if config.highlight: with tag('script'): doc.attr(src='js/highlight/highlight.pack.js') with tag('script'): doc.text("""hljs.initHighlightingOnLoad();""") def __put_html_slide_decorators(self, tag, doc, decorator, position=None, overtheme=None, current=None): """Put html data of headers, footers and sidebars. Parameters ---------- doc: Doc tag: tag decorator: {header, footer, sidebar} position: {'L','R'} sidebars position, L => left, R => right current: list """ if overtheme is not None and overtheme.custom: theme = overtheme else: theme = self.theme # decorators = getattr(self.theme, 'slide_' + decorator) decorators = getattr(theme, 'slide_' + decorator) for decor in sorted(decorators): insert = True # position check for sidebars if decorator == 'sidebar' and position is not None: for css in decorators[decor]: for key in css: if 'position' in key.lower(): pos = css[key] break insert = pos.lower() == position.lower() # active check for css in decorators[decor]: for key in css: if 'active' in key.lower(): insert = insert and css[key].lower() == 'yes' if insert: # placeholders = self.theme.get_slide_decorators_metadata(decorator=decorator, name=decor) placeholders = theme.get_slide_decorators_metadata( decorator=decorator, name=decor) for metadata in self.metadata: placeholders = self.metadata[metadata].parse( parser=self.parser, source=placeholders, toc_depth=self.metadata['toc_depth'].value, max_time=self.metadata['max_time'].value, current=current) with tag('div', klass='slide-' + decor): doc.asis(placeholders) def parse(self, config, source): """Parse presentation from source stream. Parameters ---------- config : MatisseConfig MaTiSSe configuration source: str """ complete_source = self.parser.includes(source=source) self.__get_metadata(source=complete_source) self.__get_theme(source=complete_source) new_theme = Theme() new_theme.set_from(other=self.theme) tokens = self.parser.tokenize(source=complete_source) self.__check_bad_sectioning(tokens=tokens) chapters_number = 0 sections_number = 0 subsections_number = 0 slides_number = 0 titlepage_inserted = False for chap in tokens['chapters']: chapters_number += 1 slide_local_numbers = [0, 0, 0] if chap['match'].group('expr'): chapter = Chapter(number=chapters_number, title=chap['match'].group('expr')) else: chapter = Chapter(number=chapters_number, title='') for sec in tokens['sections']: if sec['start'] >= chap['start'] and sec['start'] <= chap[ 'end_next']: sections_number += 1 slide_local_numbers[1] = 0 slide_local_numbers[2] = 0 section = Section(number=sections_number, title=sec['match'].group('expr')) for subsec in tokens['subsections']: if subsec['start'] >= sec['start'] and subsec[ 'start'] <= sec['end_next']: subsections_number += 1 slide_local_numbers[2] = 0 subsection = Subsection( number=subsections_number, title=subsec['match'].group('expr')) for sld in tokens['slides']: if '$titlepage' in sld['match'].group().lower( ) and not titlepage_inserted: slide = Slide( number=0, title='titlepage', contents=complete_source[ sld['end']:sld['end_next']]) slide.get_overtheme(parser=self.parser) if slide.overtheme.copy_from_theme is not None and slide.overtheme.copy_from_theme: slide.overtheme.copy_from( other=self.theme) self.position.update_position( presentation_theme=self.theme, overtheme=slide.overtheme) slide.set_position( position=self.position.position) subsection.add_slide(slide=slide) titlepage_inserted = True else: if sld['start'] >= subsec['start'] and sld[ 'start'] <= subsec['end_next']: slide_local_numbers[0] += 1 slide_local_numbers[1] += 1 slide_local_numbers[2] += 1 if slide_local_numbers[ 0] == 1 and config.toc_at_chap_beginning is not None: slides_number += 1 self.position.update_position( presentation_theme=self.theme) subsection.add_slide(slide=Slide( number=slides_number, position=self.position. position, title='Table of Contents', contents='$toc[depth:' + str(config. toc_at_chap_beginning) + ']')) if slide_local_numbers[ 1] == 1 and config.toc_at_sec_beginning is not None: slides_number += 1 self.position.update_position( presentation_theme=self.theme) subsection.add_slide(slide=Slide( number=slides_number, position=self.position. position, title='Table of Contents', contents='$toc[depth:' + str(config.toc_at_sec_beginning ) + ']')) if slide_local_numbers[ 2] == 1 and config.toc_at_subsec_beginning is not None: slides_number += 1 self.position.update_position( presentation_theme=self.theme) subsection.add_slide(slide=Slide( number=slides_number, position=self.position. position, title='Table of Contents', contents='$toc[depth:' + str(config. toc_at_subsec_beginning) + ']')) slides_number += 1 slide = Slide( number=slides_number, title=sld['match'].group('expr'), contents=complete_source[ sld['end']:sld['end_next']]) slide.get_overtheme(parser=self.parser) if slide.overtheme.copy_from_theme is not None and slide.overtheme.copy_from_theme: slide.overtheme.copy_from( other=self.theme) self.position.update_position( presentation_theme=self.theme, overtheme=slide.overtheme) slide.set_position( position=self.position.position) subsection.add_slide(slide=slide) section.add_subsection(subsection=subsection) chapter.add_section(section=section) self.__add_chapter(chapter=chapter) self.metadata['total_slides_number'].update_value( value=str(Subsection.slides_number)) def to_html(self, config): """Generate a html stream of the whole presentation. Parameters ---------- config : MatisseConfig MaTiSSe configuration """ doc, tag, text = Doc().tagtext() doc.asis('<!DOCTYPE html>') with tag('html'): # doc.attr(title=self.metadata['title'].value) self.__put_html_tag_head(doc=doc, tag=tag, text=text, config=config) with tag('body', onload="resetCountdown(" + str(self.metadata['max_time'].value) + ");"): doc.attr(klass='impress-not-supported') with tag('div', id='impress'): # numbering: [local_chap, local_sec, local_subsec, local_slide] current = [0, 0, 0, 0] for chapter in self.chapters: current[0] += 1 current[1] = 0 current[2] = 0 current[3] = 0 self.metadata['chaptertitle'].update_value( value=chapter.title) self.metadata['chapternumber'].update_value( value=chapter.number) for section in chapter.sections: current[1] += 1 current[2] = 0 current[3] = 0 self.metadata['sectiontitle'].update_value( value=section.title) self.metadata['sectionnumber'].update_value( value=section.number) for subsection in section.subsections: current[2] += 1 current[3] = 0 self.metadata['subsectiontitle'].update_value( value=subsection.title) self.metadata['subsectionnumber'].update_value( value=subsection.number) for slide in subsection.slides: current[3] += 1 self.metadata['slidetitle'].update_value( value=slide.title) self.metadata['slidenumber'].update_value( value=slide.number) with doc.tag('div'): chapter.put_html_attributes(doc=doc) section.put_html_attributes(doc=doc) subsection.put_html_attributes(doc=doc) slide.put_html_attributes(doc=doc) self.__put_html_slide_decorators( tag=tag, doc=doc, decorator='header', current=current, overtheme=slide.overtheme) self.__put_html_slide_decorators( tag=tag, doc=doc, decorator='sidebar', position='L', current=current, overtheme=slide.overtheme) slide.to_html(doc=doc, parser=self.parser, metadata=self.metadata, theme=self.theme, current=current) self.__put_html_slide_decorators( tag=tag, doc=doc, decorator='sidebar', position='R', current=current, overtheme=slide.overtheme) self.__put_html_slide_decorators( tag=tag, doc=doc, decorator='footer', current=current, overtheme=slide.overtheme) self.__put_html_tags_scripts(doc=doc, tag=tag, config=config) # source = re.sub(r"<li>(?P<item>.*)</li>", r"<li><span>\g<item></span></li>", source) html = indent(doc.getvalue()) return html def save(self, config, output): """Save the html form of presentation into external file. Parameters ---------- config : MatisseConfig MaTiSSe configuration output : str output path """ if not os.path.exists(output): os.makedirs(output) with open(os.path.join(output, 'index.html'), 'w') as html: html.write(self.to_html(config=config)) # copy user defined directories if set if len(self.metadata['dirs_to_copy'].value) > 0: for data in self.metadata['dirs_to_copy'].value: sync_logger = logging.getLogger('sync_logger') sync(data, os.path.join(output, data), 'sync', create=True, logger=sync_logger) # css files with open(os.path.join(output, 'css/theme.css'), 'w') as css_theme: css_theme.writelines(self.theme.css) for chapter in self.chapters: for section in chapter.sections: for subsection in section.subsections: for slide in subsection.slides: if slide.overtheme.custom: with open( os.path.join( output, 'css/slide-' + str(slide.number) + '-overtheme.css'), 'w') as css_theme: css_theme.writelines(slide.overtheme.css) return
class Slide(object): """ Slide object. """ @classmethod def reset(cls): """Reset to default state.""" return def __init__(self, number, position=None, title=None, contents=None): """" Paramters --------- number: int slide global numeration position: dict position dictionary containing {'x': posx, 'y': posy, 'z': posz, 'rotx': rotx, 'roty': roty, 'rotz': rotz, 'scale': scaling} title: str contents: str """ self.number = number self.position = None self.set_position(position) self.title = title self.contents = contents self.overtheme = Theme() return def __str__(self): strings = [str(self.title)] strings.append(str(self.contents)) return ''.join(strings) def get_overtheme(self, parser): """Get eventaul overtheme definition. Parameters ---------- parser: Parser """ codeblocks = parser.tokenizer(source=self.contents, re_search=parser.regexs['codeblock']) yamlblocks = parser.tokenizer(source=self.contents, re_search=parser.regexs['yamlblock'], exclude=codeblocks) if len(yamlblocks) > 0: self.overtheme.get(source=''.join( [block['match'].group().strip('---') for block in yamlblocks]), name='overtheme', div_id='slide-' + str(self.number)) purged_contents = self.contents[:yamlblocks[0]['start']] for b, yamlblock in enumerate(yamlblocks[:-1]): purged_contents += self.contents[ yamlblock['end']:yamlblocks[b + 1]['start']] purged_contents += self.contents[yamlblocks[-1]['end']:] self.contents = purged_contents def set_position(self, position): """Set slide position. Parameters ---------- position: dict position dictionary containing {'x': posx, 'y': posy, 'z': posz, 'rotx': rotx, 'roty': roty, 'rotz': rotz, 'scale': scaling} """ if position is not None: self.position = {} for key in position: self.position[key] = position[key] def put_html_attributes(self, doc): """Put html attibutes of the slide. Parameters ---------- doc: Doc """ doc.attr(('id', 'slide-' + str(self.number))) # doc.attr(('title', str(self.title))) doc.attr(('class', 'step slide')) doc.attr(('data-x', str(self.position['x']))) doc.attr(('data-y', str(self.position['y']))) doc.attr(('data-z', str(self.position['z']))) doc.attr(('data-scale', str(self.position['scale']))) doc.attr(('data-rotate-x', str(self.position['rotx']))) doc.attr(('data-rotate-y', str(self.position['roty']))) doc.attr(('data-rotate-z', str(self.position['rotz']))) return def to_html(self, doc, parser, metadata, theme, current): """Generate html from self. Parameters ---------- doc: Doc parser: Parser metatadata: dict presentation metadata theme: Theme() presentation theme current: list """ def _parse_env(Env, re_search, source): codeblocks = parser.tokenizer(source=source, re_search=parser.regexs['codeblock']) codes = parser.tokenizer(source=source, re_search=parser.regexs['code'], exclude=codeblocks) yamlblocks = parser.tokenizer(source=source, re_search=parser.regexs['yamlblock'], exclude=codeblocks + codes) envs = parser.tokenizer(source=source, re_search=re_search, exclude=codeblocks + yamlblocks + codes) if len(envs) > 0: parsed_source = source[:envs[0]['start']] for e, env in enumerate(envs[:-1]): current = Env(source=env['match'].group()) parsed_source += current.to_html( ) + source[env['end']:envs[e + 1]['start']] if Env is Video: if self.overtheme.custom: current = Env(source=envs[-1]['match'].group(), theme=self.overtheme) else: current = Env(source=envs[-1]['match'].group(), theme=theme) else: current = Env(source=envs[-1]['match'].group()) parsed_source += current.to_html() + source[envs[-1]['end']:] return parsed_source return source html = self.contents for meta in metadata: html = metadata[meta].parse(parser=parser, source=html, toc_depth=metadata['toc_depth'].value, max_time=metadata['max_time'].value, current=current) html = _parse_env(Env=Box, re_search=Box.regexs['box'], source=html) html = _parse_env(Env=Note, re_search=Note.regexs['note'], source=html) html = _parse_env(Env=Figure, re_search=Figure.regexs['figure'], source=html) html = _parse_env(Env=Table, re_search=Table.regexs['table'], source=html) html = _parse_env(Env=Video, re_search=Video.regexs['video'], source=html) html = _parse_env(Env=Columns, re_search=Columns.regexs['columns'], source=html) with doc.tag('div', klass='slide-content'): doc.asis(markdown2html(source=html)) return