# -*- coding: utf-8 -*-
import pylatex as pltx
from abc import abstractmethod
from .base import TexBase
from .preamble import append_packages, append_cover
from .utils import section
_default_geometry_options_ = {
"tmargin": "1.5cm",
"lmargin": "1.5cm",
"rmargin": "1.5cm"
}
[docs]class BaseTexDoc(TexBase):
"""
Base class for all document types.
"""
documentclass = None
def __init__(self, *args, geometry_options=None, title=None, author=None,
date=False, doc=None, content=None, **kwargs):
super().__init__(*args, **kwargs)
isroot = self.is_root()
if title is not None:
assert isroot, "Only the root object can have a title!"
if author is not None:
assert isroot, "Only the root object can have an author!"
title = title if title is not None else 'Documentation'
self._title = title
self._author = author
self._date = date
self._content = content if content is not None else []
if geometry_options is None:
geometry_options = _default_geometry_options_
self._geometry_options = geometry_options
self._preamble = [] if isroot else None
self._doc = doc
@property
def title(self) -> str:
"""
Returns the title of the document.
"""
return self.root()._title
@property
def doc(self) -> pltx.Document:
"""
Returns the underlying document instance.
"""
return self.root()._doc
@property
def preamble(self) -> list:
"""
Returns the underlying document instance.
"""
return self.root()._preamble
[docs] @abstractmethod
def init_doc(self, **kwargs) -> pltx.Document:
"""
Override this to create a new document type.
"""
...
def _adopt_child_(self, child):
if isinstance(child, BaseTexDoc):
child.parent = self
return child
def is_nested(self, **kwargs) -> bool:
"""
Returns `True` if the current section has subsections, `False` otherwise.
"""
level = kwargs.get('_level', self.depth)
return (self.has_children() or len(self.content) > 0) and level <= 3
def has_children(self):
return any(map(lambda v: isinstance(v, BaseTexDoc), self.values()))
def _append2doc_(self, doc, *args, level=None, nosection=False, **kwargs):
level = level if level is not None else self.depth
if self.is_nested(_level=level) and not nosection:
with doc.create(section(self.key, level=level)):
for c in self.content:
if hasattr(c, '_append2doc_'):
doc = c._append2doc_(doc, level=level, nosection=True)
else:
doc.append(c)
else:
for c in self.content:
if hasattr(c, '_append2doc_'):
doc = c._append2doc_(doc, level=level, nosection=True)
else:
doc.append(c)
return doc
def build(self, *args, **kwargs) -> pltx.Document:
"""
Builds and returns an instance of :class:`pylatex.document.Document`.
Example
-------
>>> from latexdocs import Document
>>> doc = Document(title='Title', author='Author', date=True)
>>> doc['Section 1'].append('Some regular text')
>>> doc.build()
"""
doc = kwargs.get('_doc', None)
level = kwargs.get('_level', None)
if doc is None:
assert self.is_root()
doc = self.init_doc()
return self.build(_doc=doc, _level=0)
else:
assert isinstance(level, int)
if level == 0:
doc = self._append2doc_(doc, level=level, nosection=True)
else:
doc = self._append2doc_(doc, level=level)
for v in self.values():
if isinstance(v, BaseTexDoc):
v.build(_doc=doc, _level=level+1)
return doc
def generate_pdf(self, *args, clean_tex=False, compiler='pdflatex', **kwargs):
"""
Builds the document and generates a pdf in one go.
Parameters
----------
args : tuple, Optional
Extra positional arguments are forwarded to `pylatex.Document.generate_pdf`.
clean_tex : bool, Optional
Default is False.
compiler : str, Optional
The compiler to use. Default is `pdflatex`. See the docs of PyLaTeX
for all the available options.
kwargs : tuple, Optional
Extra kyeword arguments are forwarded to `pylatex.Document.generate_pdf`.
Example
-------
>>> from latexdocs import Document
>>> doc = Document(title='Title', author='Author', date=True)
>>> doc['Section 1'].append('Some regular text')
>>> doc.generate_pdf('filename', compiler='pdflatex')
"""
self.build().generate_pdf(*args, clean_tex=clean_tex, compiler=compiler, **kwargs)
[docs]class Document(BaseTexDoc):
"""
Base class to store information about a document.
Parameters
----------
geometry_options : dict, Optional
A dictionary containing information about the geometry of the
document. Default is None.
title : str, Optional
The title of the document. Default is None.
author : str, Optional
The author of the document. Default is None.
date : bool, Optional
If True, a date is show on the title page. Default is False.
doc : :class:`pylatex.document.Document`, Optional
An instance of `pylatex.Document`, if you already have one.
Default is None.
content : list, Optional
Content related to the current section. Default is None.
Notes
-----
The implementation is not foolproof. For instance, if you ask for the date
to be shown but provide no title or author, you are gonna experience problems
which I don't care about (the author). Don't be dumb.
Example
-------
>>> from latexdocs import Document
>>> doc = Document(title='Title', author='Author', date=True)
See Also
--------
:class:`linkeddeepdict.LinkedDeepDict`
:class:`pylatex.document.Document`
"""
documentclass = 'article'
def init_doc(self, **kwargs) -> pltx.Document:
"""
Initializes the document. This covers appending packages
and the preamble.
"""
dcls = self.__class__.documentclass
kwargs['documentclass'] = kwargs.get('documentclass', dcls)
kwargs['geometry_options'] = self._geometry_options
doc = pltx.Document(**kwargs)
doc = append_packages(doc)
for c in self.preamble:
doc.preamble.append(c)
doc = append_cover(doc, self._title, self._author, self._date)
return doc
[docs]class Article(Document):
"""
Top level class for articles.
Example
-------
>>> from latexdocs import Article
>>> doc = Article(title='Title', author='Author', date=True)
See Also
--------
:class:`linkeddeepdict.LinkedDeepDict`
:class:`pylatex.document.Document`
"""
documentclass = 'article'
[docs]class Book(Document):
"""
Top level class for books.
Example
-------
>>> from latexdocs import Book
>>> doc = Book(title='Title', author='Author', date=True)
See Also
--------
:class:`linkeddeepdict.LinkedDeepDict`
:class:`pylatex.document.Document`
"""
documentclass = 'book'