Beispiel #1
0
    def __init__(self, filename, filepath='.', doc_type='article', options=(), **kwoptions):
        r"""
        Args:
            filename (str): Name of the file without extension.
            filepath (str): Path where the files will be saved and compiled to pdf.
            doc_type (str): Any document type LaTeX supports, like 'article', 'standalone', etc.
            options (Union[Tuple[str], str, TexObject]): Any options that goes between brackets. See template further.
            kwoptions (keyword options of the document type): Options should be strings. The dict is converted to string
            when building to tex. See template below.

        The doc_type, options and kwoptions arguments will be compiled in the following way:
            \documentclass[*options, **kwoptions]{doc_type}
        """
        super().__init__('document')
        self.filename = filename
        self.filepath = filepath
        self.file = TexFile(filename, filepath)

        self.doc_class = TexCommand('documentclass',
                                    doc_type,
                                    options=options,
                                    options_pos='first',
                                    **kwoptions)

        self.add_package('inputenc', 'utf8')
        self.set_margins('2.5cm')
Beispiel #2
0
 def __init__(self,
              filename,
              filepath='.',
              output_filename=None,
              output_filepath=None):
     """
     Args:
         filename (str): Name of the input tex file without extension.
         filepath (str): Path where the input file is.
         output_filename (str or None): Name of the output file without the extension. If None, the name of the input file appended with '_rendered' will be used. If the output filename is the same as the input filename, the input filename will be overwrited, which can be useful but also dangerous if there is a problem in the code.
         output_filepath (str): Path where the rendered files will be placed. If None, the path of the input file will be used.
     """
     self.input_file = TexFile(filename, filepath)
     if output_filename is None:
         output_filename = filename + '_rendered'
     if output_filepath is None:
         output_filepath = filepath
     self.output_file = TexFile(output_filename, output_filepath)
     self.anchors = {}
Beispiel #3
0
class Document(TexEnvironment):
    """
    Tex document class.
    Has a body, a preamble and a dict of packages updated recursively with other TexEnvironment nested inside the body.
    The 'build' method writes all text to a .tex file and compiles it to pdf.
    """
    def __init__(self, filename, filepath='.', doc_type='article', options=(), **kwoptions):
        r"""
        Args:
            filename (str): Name of the file without extension.
            filepath (str): Path where the files will be saved and compiled to pdf.
            doc_type (str): Any document type LaTeX supports, like 'article', 'standalone', etc.
            options (Union[Tuple[str], str, TexObject]): Any options that goes between brackets. See template further.
            kwoptions (keyword options of the document type): Options should be strings. The dict is converted to string
            when building to tex. See template below.

        The doc_type, options and kwoptions arguments will be compiled in the following way:
            \documentclass[*options, **kwoptions]{doc_type}
        """
        super().__init__('document')
        self.filename = filename
        self.filepath = filepath
        self.file = TexFile(filename, filepath)

        self.doc_class = TexCommand('documentclass',
                                    doc_type,
                                    options=options,
                                    options_pos='first',
                                    **kwoptions)

        self.add_package('inputenc', 'utf8')
        self.set_margins('2.5cm')

    def __repr__(self):
        return f'Document {self.filename}'

    def set_margins(self, margins='2.5cm', top=None, bottom=None, left=None, right=None):
        """
        Sets margins of the document. Default is 2.5cm on all sides.

        Args:
            margins (str): Default value for all sides.
            top, bottom, left, right (str, any valid LaTeX length): Overrides the 'margins' argument with the specified
            length.
        """
        top = top or margins
        bottom = bottom or margins
        left = left or margins
        right = right or margins

        self.add_package('geometry', top=top, bottom=bottom, left=left, right=right)

    def new_section(self, name, label=''):
        """
        Create a new LaTeX section.

        Args:
            name (str): Name of the section.
            label (str): Label of the section to refer to.
        """
        return self.new(Section(name, label=label))

    def build(self, save_to_disk=True, compile_to_pdf=True, show_pdf=True):
        """
        Builds the document to a tex file and optionally compiles it into tex and show the output pdf in the default
        pdf reader of the system.

        Args:
            save_to_disk (bool): If True, the built tex will be save to disk automatically. Else, one can recover the
            tex string from the return of the current method.
            compile_to_pdf (bool): If True, automatically call pdflatex to compile the generated tex file to pdf.
            show_pdf (bool): If True, the default pdf reader will be called to show the compiled pdf. This may not work
            well with non-read-only pdf viewer such as Acrobat Reader or Foxit Reader.

        Returns:
            The tex string of the file.
        """
        tex = super().build()

        tex = build(self.doc_class) + '\n' + self.build_preamble() + '\n' + tex
        if save_to_disk:
            self.file.save(tex)

        if compile_to_pdf:
            self.file.save(tex)
            self.file.compile_to_pdf()

        if show_pdf:
            open_file_with_default_program(self.filename, self.filepath)

        return tex
Beispiel #4
0
class Document(TexEnvironment):
    """
    Tex document class.
    Has a body, a preamble and a dict of packages updated recursively with other TexEnvironment nested inside the body.
    The 'build' method writes all text to a .tex file and compiles it to pdf.
    """
    def __init__(self,
                 filename,
                 filepath='.',
                 doc_type='article',
                 options=(),
                 **kwoptions):
        r"""
        Args:
            filename (str): Name of the file without extension.
            filepath (str): Path where the files will be saved and compiled to pdf.
            doc_type (str): Any document type LaTeX supports, like 'article', 'standalone', etc.
            options (Union[Tuple[str], str, TexObject]): Any options that goes between brackets. See template further.
            kwoptions (keyword options of the document type): Options should be strings. The dict is converted to string
            when building to tex. See template below.

        The doc_type, options and kwoptions arguments will be compiled in the following way:
            \documentclass[*options, **kwoptions]{doc_type}
        """
        super().__init__('document')
        self.filename = filename
        self.filepath = filepath
        self.file = TexFile(filename, filepath)

        self.doc_class = TexCommand('documentclass',
                                    doc_type,
                                    options=options,
                                    options_pos='first',
                                    **kwoptions)

        self.add_package('inputenc', 'utf8')
        self.set_margins('2.5cm')

    def __repr__(self):
        return f'Document {self.filename}'

    def set_margins(self,
                    margins='2.5cm',
                    top=None,
                    bottom=None,
                    left=None,
                    right=None):
        """
        Sets margins of the document. Default is 2.5cm on all sides.

        Args:
            margins (str): Default value for all sides.
            top, bottom, left, right (str, any valid LaTeX length): Overrides the 'margins' argument with the specified
            length.
        """
        top = top or margins
        bottom = bottom or margins
        left = left or margins
        right = right or margins

        self.add_package('geometry',
                         top=top,
                         bottom=bottom,
                         left=left,
                         right=right)

    def new_section(self, name, label=''):
        """
        Create a new LaTeX section.

        Args:
            name (str): Name of the section.
            label (str): Label of the section to refer to.
        """
        return self.new(Section(name, label=label))

    def build(self,
              save_to_disk=True,
              compile_to_pdf=True,
              show_pdf=True,
              delete_files=list(),
              build_from_dir='cwd'):
        r"""
        Builds the document to a tex file and optionally compiles it into tex and show the output pdf in the default pdf reader of the system.

        Args:
            save_to_disk (bool):
                If True, the built tex will be save to disk automatically. Else, one can recover the tex string from the return of the current method.
            compile_to_pdf (bool):
                If True, automatically call pdflatex to compile the generated tex file to pdf. Only used if 'save_to_disk' is True.
            show_pdf (bool):
                If True, the default pdf reader will be called to show the compiled pdf. This may not work well with non-read-only pdf viewer such as Acrobat Reader or Foxit Reader. Only used if 'save_to_disk' and 'compile_to_pdf' are True.
            delete_files (Union[str, Iterable[str]]):
                Extensions of the files to delete after compilation. By default no files saved on disk are deleted. Valid extensions are 'tex', 'aux', 'log' and 'pdf'. 'all' is also accepted and will delete everything except the pdf.
            build_from_dir (str, either 'source' or 'cwd'):
                Directory to build from. With the 'source' option, pdflatex will be called from the directory containing the TeX file, like this:
                    ~/some/path/to/tex_file> pdflatex './filename.tex'
                With the 'cwd' option, pdflatex will be called from the current working directory, like this:
                    ~/some/path/to/cwd> pdflatex 'filepath/filename.tex'
                This can be important if you include content in the TeX file, such as with the command \input{<path_to_some_file>}, where 'path_to_some_file' should be relative to the directory where pdflatex is called.

        Returns:
            The tex string of the file.
        """
        tex = super().build()

        tex = build(self.doc_class) + '\n' + self.build_preamble() + '\n' + tex
        if save_to_disk:
            self.file.save(tex)

            if compile_to_pdf:
                self.file.compile_to_pdf(build_from_dir=build_from_dir)

                if show_pdf:
                    open_file_with_default_program(self.filename,
                                                   self.filepath)

                if isinstance(delete_files, str):
                    if delete_files == 'all':
                        delete_files = ['tex', 'aux', 'log']
                    else:
                        delete_files = [delete_files]

                for ext in delete_files:
                    if ext in ['tex', 'aux', 'log', 'pdf']:
                        os.remove(f'{self.filepath}/{self.filename}.{ext}')

        return tex
Beispiel #5
0
class Template:
    """
    Class that allows to insert TeX commands inside an already existing tex file and then compile it. Useful to make figures and tables with python2latex and insert them into your project without needing to copy and paste.

    To tell python2latex where to insert an object, write the line
        %! python2latex-anchor = *the_name_of_your_object_here*
    The script will automatically insert the tex under this anchor and add a closing statement as
        %! python2latex-end-anchor = *the_name_of_your_object_here*

    The preamble will also be automatically updated, where you can find added commands right under the anchor
        %! python2latex-preamble

    See the examples for a more complete example.
    """
    def __init__(self,
                 filename,
                 filepath='.',
                 output_filename=None,
                 output_filepath=None):
        """
        Args:
            filename (str): Name of the input tex file without extension.
            filepath (str): Path where the input file is.
            output_filename (str or None): Name of the output file without the extension. If None, the name of the input file appended with '_rendered' will be used. If the output filename is the same as the input filename, the input filename will be overwrited, which can be useful but also dangerous if there is a problem in the code.
            output_filepath (str): Path where the rendered files will be placed. If None, the path of the input file will be used.
        """
        self.input_file = TexFile(filename, filepath)
        if output_filename is None:
            output_filename = filename + '_rendered'
        if output_filepath is None:
            output_filepath = filepath
        self.output_file = TexFile(output_filename, output_filepath)
        self.anchors = {}

    def _load_tex_file(self):
        """
        Returns the loaded tex file as a list of strings.
        """
        with open(self.input_file.path, 'r', encoding='utf8') as file:
            return [line.strip() for line in file]

    def _split_preamble(self, text):
        """
        Returns the preamble and the rest of the document.
        """
        for i, line in enumerate(text):
            if r'\begin{document}' in line:
                begin_document_line = i
                break
        return text[:begin_document_line], text[begin_document_line:]

    def _insert_tex_at_anchors(self, doc):
        anchors_position = {}
        for i, line in enumerate(doc):
            if line.startswith('%! python2latex-anchor'):
                _, anchor_name = line.replace(' ', '').split('=')
                anchors_position[anchor_name] = (i, None)

            if line.startswith('%! python2latex-end-anchor'):
                anchors_position[anchor_name] = (
                    anchors_position[anchor_name][0], i)

        for i, (anchor_name, (start,
                              end)) in enumerate(anchors_position.items()):
            if end:
                del doc[start + 1:end + 1]
            doc.insert(start + 1 + i, self.anchors[anchor_name])
            doc.insert(start + 2 + i,
                       f'%! python2latex-end-anchor = {anchor_name}')

    def _update_preamble(self, preamble):
        # Removing old python2latex preamble
        for i, line in enumerate(preamble):
            if line == '%! python2latex-preamble':
                break
        else:
            preamble[i:]

        # Adding only new lines to preamble
        anchor_preambles = set(line for obj in self.anchors.values()
                               for line in obj.build_preamble().split('\n'))
        lines_to_add = []
        for line in anchor_preambles:
            if line not in preamble and line:
                lines_to_add.append(line)

        if lines_to_add:
            preamble.extend(['%! python2latex-preamble'] + lines_to_add)

    def render(self,
               compile_to_pdf=True,
               show_pdf=True,
               build_from_dir='source'):
        """
        Loads the input files, parses the tex to find the anchors, inserts the code generated by python2latex then saves it to disk.

        Args:
            compile_to_pdf (bool):
                If True, automatically call pdflatex to compile the generated tex file to pdf.
            show_pdf (bool):
                If True, the default pdf reader will be called to show the compiled pdf. This may not work well with non-read-only pdf viewer such as Acrobat Reader or Foxit Reader. Suggested readers are SumatraPDF on Windows and Okular or Evince on Linux.
            build_from_dir (str, either 'source' or 'cwd'):
                Directory to build from. With the 'source' option, pdflatex will be called from the directory containing the TeX file, like this:
                    ~/some/path/to/tex_file> pdflatex './filename.tex'
                With the 'cwd' option, pdflatex will be called from the current working directory, like this:
                    ~/some/path/to/cwd> pdflatex 'filepath/filename.tex'
                This can be important if you include content in the TeX file, such as with the command \input{<path_to_some_file>}, where 'path_to_some_file' should be relative to the directory where pdflatex is called.
        """
        tex = self._load_tex_file()
        preamble, doc = self._split_preamble(tex)
        self._insert_tex_at_anchors(doc)
        self._update_preamble(preamble)
        tex = '\n'.join(build(line) for line in preamble + doc)

        self.output_file.save(tex)

        if compile_to_pdf:
            self.output_file.compile_to_pdf(build_from_dir=build_from_dir)

            if show_pdf:
                open_file_with_default_program(self.output_file.filename,
                                               self.output_file.filepath)