diff --git a/pubplot/doc_sizes_cache.json b/pubplot/doc_sizes_cache.json new file mode 100644 index 0000000..3e35ee3 --- /dev/null +++ b/pubplot/doc_sizes_cache.json @@ -0,0 +1,167 @@ +{ + "ieee_infocom": { + "columnwidth": 252.0, + "textwidth": 516.0, + "caption": 8.0, + "tiny": 5.0, + "scriptsize": 7.0, + "footnotesize": 8.0, + "small": 9.0, + "normalsize": 10.0, + "large": 12.0, + "Large": 14.0, + "LARGE": 17.0, + "huge": 20.0, + "Huge": 24.0 + }, + "ieee_conf": { + "columnwidth": 252.0, + "textwidth": 516.0, + "caption": 8.0, + "tiny": 5.0, + "scriptsize": 7.0, + "footnotesize": 8.0, + "small": 9.0, + "normalsize": 10.0, + "large": 12.0, + "Large": 14.0, + "LARGE": 17.0, + "huge": 20.0, + "Huge": 24.0 + }, + "ieee_conf_compsoc": { + "columnwidth": 243.91125, + "textwidth": 505.89, + "caption": 8.03, + "tiny": 5.01874, + "scriptsize": 7.02625, + "footnotesize": 8.03, + "small": 9.03374, + "normalsize": 10.03749, + "large": 12.045, + "Large": 14.05249, + "LARGE": 17.06374, + "huge": 20.075, + "Huge": 24.09 + }, + "ieee_jrnl": { + "columnwidth": 252.0, + "textwidth": 516.0, + "caption": 8.0, + "tiny": 5.0, + "scriptsize": 7.0, + "footnotesize": 8.0, + "small": 9.0, + "normalsize": 10.0, + "large": 12.0, + "Large": 14.0, + "LARGE": 17.0, + "huge": 20.0, + "Huge": 24.0 + }, + "ieee_jrnl_compsoc": { + "columnwidth": 252.94499, + "textwidth": 517.935, + "caption": 8.03, + "tiny": 5.01874, + "scriptsize": 7.02625, + "footnotesize": 8.03, + "small": 9.03374, + "normalsize": 9.53561, + "large": 12.045, + "Large": 14.05249, + "LARGE": 17.06374, + "huge": 20.075, + "Huge": 24.09 + }, + "ieee_jrnl_comsoc": { + "columnwidth": 252.0, + "textwidth": 516.0, + "caption": 8.0, + "tiny": 5.0, + "scriptsize": 7.0, + "footnotesize": 8.0, + "small": 9.0, + "normalsize": 10.0, + "large": 12.0, + "Large": 14.0, + "LARGE": 17.0, + "huge": 20.0, + "Huge": 24.0 + }, + "ieee_jrnl_transmag": { + "columnwidth": 252.0, + "textwidth": 516.0, + "caption": 8.0, + "tiny": 5.0, + "scriptsize": 7.0, + "footnotesize": 8.0, + "small": 9.0, + "normalsize": 10.0, + "large": 12.0, + "Large": 14.0, + "LARGE": 17.0, + "huge": 20.0, + "Huge": 24.0 + }, + "acm_sigconf": { + "columnwidth": 241.14749, + "textwidth": 506.295, + "caption": 9.0, + "tiny": 5.0, + "scriptsize": 6.0, + "footnotesize": 7.0, + "small": 8.0, + "normalsize": 9.0, + "large": 10.0, + "Large": 10.95, + "LARGE": 12.0, + "huge": 14.4, + "Huge": 17.28 + }, + "usenix": { + "columnwidth": 241.02039, + "textwidth": 505.89, + "caption": 10.0, + "tiny": 5.0, + "scriptsize": 7.0, + "footnotesize": 8.0, + "small": 9.0, + "normalsize": 10.0, + "large": 12.0, + "Large": 14.4, + "LARGE": 17.28, + "huge": 20.74, + "Huge": 24.88 + }, + "sbc": { + "columnwidth": 426.79135, + "textwidth": 426.79135, + "caption": 10.0, + "tiny": 6.0, + "scriptsize": 8.0, + "footnotesize": 10.0, + "small": 10.95, + "normalsize": 12.0, + "large": 14.4, + "Large": 17.28, + "LARGE": 20.74, + "huge": 24.88, + "Huge": 24.88 + }, + "article": { + "columnwidth": 345.0, + "textwidth": 345.0, + "caption": 10.0, + "tiny": 5.0, + "scriptsize": 7.0, + "footnotesize": 8.0, + "small": 9.0, + "normalsize": 10.0, + "large": 12.0, + "Large": 14.4, + "LARGE": 17.28, + "huge": 20.74, + "Huge": 24.88 + } +} \ No newline at end of file diff --git a/pubplot/document.py b/pubplot/document.py index 696a60c..f012d25 100644 --- a/pubplot/document.py +++ b/pubplot/document.py @@ -24,7 +24,7 @@ from pubplot.axes import PubAxes from pubplot.figure import PubFigure from pubplot.helpers import RCParams -from pubplot.latex import get_document_sizes +from pubplot.latex import get_document_sizes, _check_latex_installation from pubplot.styles import dichromatic inches_per_pt = 1.0 / 72.27 @@ -110,8 +110,6 @@ def __init__(self, document_class, style=None): # check https://matplotlib.org/users/customizing.html for some options self.style = { - 'pgf.texsystem': 'pdflatex', - 'text.usetex': True, 'figure.dpi': 600, # recommended DPI for journal prints # fonts (empty lists inherit from document) @@ -126,26 +124,37 @@ def __init__(self, document_class, style=None): 'legend.fontsize': self.caption, 'xtick.labelsize': self.caption, 'ytick.labelsize': self.caption, - - "pgf.preamble": [ - r"\usepackage[utf8x]{inputenc}", - r"\usepackage[T1]{fontenc}", - ] } + + if(_check_latex_installation()): + # Only use tex if latex is installed + self.style.update({ + 'pgf.texsystem': 'pdflatex', + 'text.usetex': True, + # Use utf8 instead of utf8x + # https://tex.stackexchange.com/questions/13067/utf8x-vs-utf8-inputenc + "pgf.preamble": "\n".join([ + r"\usepackage[utf8]{inputenc}", + r"\usepackage[T1]{fontenc}", + ]) + }) + if style is not None: self.update_style(style) - # document_class['packages'] is natively sent to pylatex, but we also - # need matplotlib to be aware of them. - preamble = self.style['pgf.preamble'] - for p in document_class.get('packages', []): - if isinstance(p, str): - preamble.append(r"\usepackage{{{}}}".format(p)) - elif hasattr(p, 'dumps'): - # pylatex package object - preamble.append(p.dumps()) - else: - raise NotImplementedError(p) + if(_check_latex_installation()): + # document_class['packages'] is natively sent to pylatex, but we also + # need matplotlib to be aware of them. + preamble = self.style['pgf.preamble'] + for p in document_class.get('packages', []): + if isinstance(p, str): + preamble += "\n" + r"\usepackage{{{}}}".format(p) + elif hasattr(p, 'dumps'): + # pylatex package object + preamble += "\n" + r"{}".format(p.dumps()) + else: + raise NotImplementedError(p) + self.style['pgf.preamble'] = preamble def temporary_style(self, new_style): """Returns a context manager which updates the current document style diff --git a/pubplot/document_classes.py b/pubplot/document_classes.py index 6625c45..6e3558a 100644 --- a/pubplot/document_classes.py +++ b/pubplot/document_classes.py @@ -19,61 +19,80 @@ from pylatex import Package, NoEscape ieee_infocom = { + 'sty_name': 'ieee_infocom', 'documentclass': 'IEEEtran', 'document_options': ['10pt', 'conference', 'letterpaper'], 'packages': [Package(NoEscape('times'))], } ieee_conf = { + 'sty_name': 'ieee_conf', 'documentclass': 'IEEEtran', 'document_options': ['conference'], 'packages': [Package(NoEscape('times'))], } ieee_conf_compsoc = { + 'sty_name': 'ieee_conf_compsoc', 'documentclass': 'IEEEtran', 'document_options': ['conference', 'compsoc'], 'packages': [Package(NoEscape('times'))], } ieee_jrnl = { + 'sty_name': 'ieee_jrnl', 'documentclass': 'IEEEtran', 'document_options': ['journal'], 'packages': [Package(NoEscape('times'))], } ieee_jrnl_compsoc = { + 'sty_name': 'ieee_jrnl_compsoc', 'documentclass': 'IEEEtran', 'document_options': ['10pt', 'journal', 'compsoc'], 'packages': [Package(NoEscape('times'))], } ieee_jrnl_comsoc = { + 'sty_name': 'ieee_jrnl_comsoc', 'documentclass': 'IEEEtran', 'document_options': ['journal', 'comsoc'], 'packages': [Package(NoEscape('times'))], } ieee_jrnl_transmag = { + 'sty_name': 'ieee_jrnl_transmag', 'documentclass': 'IEEEtran', 'document_options': ['journal', 'transmag'], 'packages': [Package(NoEscape('times'))], } acm_sigconf = { + 'sty_name': 'acm_sigconf', 'documentclass': 'acmart', 'document_options': 'sigconf' } usenix = { + 'sty_name': 'usenix', 'documentclass': 'article', 'document_options': ['letterpaper','twocolumn','10pt'], 'packages': [Package(NoEscape('usenix'))], } sbc = { + 'sty_name': 'sbc', 'documentclass': 'article', 'document_options': ['12pt'], 'packages': [Package(NoEscape('sbc-template'))], 'data': [NoEscape(r'\address{a}')] } + +article = { + 'sty_name': 'article' +} + +all_document_sizes = [ + ieee_infocom, ieee_conf, ieee_conf_compsoc, ieee_jrnl, ieee_jrnl_compsoc, + ieee_jrnl_comsoc, ieee_jrnl_transmag, acm_sigconf, usenix, sbc, article +] diff --git a/pubplot/figure.py b/pubplot/figure.py index 77dc0a2..d7f5762 100644 --- a/pubplot/figure.py +++ b/pubplot/figure.py @@ -23,6 +23,7 @@ from pubplot.axes import PubAxes from pubplot.helpers import RCParamWrapper +from pubplot.latex import _check_latex_installation class PubFigure(RCParamWrapper): @@ -48,7 +49,7 @@ def add_subplot(self, *args, **kwargs): ax = self.fig.add_subplot(*args, **kwargs) return PubAxes(ax, self.rc) - def save(self, name, pdf=True, pgf=True): + def save(self, name, pdf=True, pgf=True, svg=True, bbox_inches='tight', pad_inches=0): """Save figure to pgf and pdf. By default it saves the figure in both pdf and pgf, but this behavior @@ -59,12 +60,22 @@ def save(self, name, pdf=True, pgf=True): pdf: if True saves figure in pdf format pgf: if True saves figure in pgf format """ + kw = {'bbox_inches': bbox_inches, 'pad_inches': pad_inches} with mpl.rc_context(rc=self.rc.get_rc_to_function('save')): - canvas = FigureCanvasPgf(self.fig) - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - kw = {'bbox_inches': 'tight', 'pad_inches': 0} - if pgf: - canvas.print_figure(name + '.pgf', **kw) - if pdf: - canvas.print_figure(name + '.pdf', **kw) + if(_check_latex_installation()): + canvas = FigureCanvasPgf(self.fig) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + if pgf: + canvas.print_figure(name + '.pgf', **kw) + if pdf: + canvas.print_figure(name + '.pdf', **kw) + if svg: + canvas.print_figure(name + '.svg', **kw) + else: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + if(pdf): + self.fig.savefig(name + '.pdf', **kw) + if(svg): + self.fig.savefig(name + '.svg', **kw) diff --git a/pubplot/latex.py b/pubplot/latex.py index 3ebf55c..baf7118 100644 --- a/pubplot/latex.py +++ b/pubplot/latex.py @@ -16,16 +16,66 @@ # # latex.py +import glob +import json import os -from pylatex import Document, NoEscape, Command, Package -import tempfile import subprocess -import glob +import tempfile + +from pylatex import Command, Document, NoEscape, Package + +from pubplot.document_classes import all_document_sizes LATEX_BUILT_IN_SIZES = ['tiny', 'scriptsize', 'footnotesize', 'small', 'normalsize', 'large', 'Large', 'LARGE', 'huge', 'Huge'] LOG_PATTERN = '<<<>>>' +_cache_path = os.path.join(os.path.dirname(__file__), "doc_sizes_cache.json") +_cached_document_sizes = dict() +_cache_up_to_date = False + + +def _check_latex_installation(): + r""" + Ideally this should be part of pylatex + returns True if latex is found + """ + + from distutils.spawn import find_executable + + compilers = ('pdflatex', 'latexmk') + for compiler in compilers: + if find_executable(compiler): + return True + + return False + + +def _build_update_doc_size_cache(): + r""" + Requires latex installation + The idea is that we use this function to populate the cache + and ship the cache with the pubplot repository + """ + temp_sizes = dict() + for doc_class in all_document_sizes: + temp_sizes[doc_class['sty_name']] = get_document_sizes(doc_class) + + f = open(_cache_path, 'w') + json.dump(temp_sizes, f, indent=4) + f.close() + + +def _read_doc_size_cache(force=False): + global _cache_up_to_date + if(os.path.exists(_cache_path) and (not _cache_up_to_date or force)): + f = open(_cache_path, 'r') + _cached_document_sizes.update(json.load(f)) + f.close() + _cache_up_to_date = True + return _cache_up_to_date == True + + def get_document_sizes(document_class): """Get useful document sizes given a LaTeX document class. @@ -95,6 +145,11 @@ def get_document_sizes(document_class): - caption """ + if(not _check_latex_installation()): + if(_read_doc_size_cache()): + if(document_class['sty_name'] in _cached_document_sizes): + return _cached_document_sizes[document_class['sty_name']] + def log_with_name(name, value): return '\\wlog{{{}{}={}}}'.format(LOG_PATTERN, name, value) @@ -127,6 +182,7 @@ def log_text_size(text_size_name): temp_doc_name = next(tempfile._get_candidate_names()) document_kwargs = document_class.copy() + sty_name = document_kwargs.pop('sty_name', 'article') packages = document_kwargs.pop('packages', []) doc = Document(temp_doc_name, **document_kwargs) @@ -162,3 +218,7 @@ def log_text_size(text_size_name): list(map(os.remove, glob.glob(temp_doc_name + '.*'))) return sizes_dict + + +if(__name__ == "__main__"): + _build_update_doc_size_cache() diff --git a/setup.py b/setup.py index f70cef5..81b22a9 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='pubplot', - version='0.2.4', + version='0.2.5', description='Seamless LaTeX and Matplotlib integration for publication plots', long_description=readme, packages=find_packages(), @@ -17,6 +17,9 @@ author_email='hugo@sadok.com.br', keywords=['matplotlib', 'latex', 'pgf'], include_package_data=True, + package_data= { + '': ['*.json'] + }, install_requires=[ 'matplotlib', 'pylatex',