Dependencies

TeX Live

pdf2svg

  • Clone repository dawbarton/pdf2svg
  • Add dist-64bits subfolder to PATH environment variable
  • Convert PDF to SVG

Execute Code

  • Install Obsidian extension via Comminity plugins, Webstore
  • Generate LaTeX figure from Obsidian code block

Python

Setup Execute Code

Open Language-Specific Settings > Python

Inject python code

import os, contextlib
 
def save_source_to_file(latex_source, destination):
    file_path = os.path.join(destination,'figure.tex')
    with open(file_path, 'w') as source_file:
        print(latex_source, file=source_file)
    return file_path
 
@contextlib.contextmanager
def temporary_working_directory(path):
    current_dir = os.getcwd()
    try:
        os.chdir(path)
        yield
    finally:
        os.chdir(current_dir)
 
def is_standalone_class(latex_source):
    import re
    return re.search(r'\\documentclass\s*(?:\[[^\]]*\])?\s*\{standalone\}', latex_source)
 
def compile_and_crop_figure(latex_source, attachments_folder, compiler="pdflatex", escape_shell=False, rerun=0):
    import subprocess, tempfile
    intermediates_folder = tempfile.mkdtemp()
    input_file = save_source_to_file(latex_source, intermediates_folder)
    with temporary_working_directory(attachments_folder):
        subprocess.run(['texfot', 'pdflatex', 
            f'-output-directory={intermediates_folder}', input_file])
        for _ in range(rerun):
            subprocess.run(['texfot', 'pdflatex',
                f'-output-directory={intermediates_folder}', input_file])
    with temporary_working_directory(intermediates_folder):
        if not is_standalone_class(latex_source):
            os.rename('figure.pdf', 'figure_precrop.pdf')
            subprocess.run('pdfcrop figure_precrop.pdf figure.pdf', shell=True)
        subprocess.run('pdf2svg figure.pdf figure.svg', shell=True)
    return intermediates_folder
 
def print_figure(filename, folder, vault_temporary_folder):
    import shutil, uuid
    source_file = os.path.join(folder, filename)
    vault_filename = str(uuid.uuid4()) + '.svg'
    vault_relative_file = f'/{vault_temporary_folder}/{vault_filename}'
    shutil.copy2(source_file, @vault_path + vault_relative_file)
    @html('<img src="' + @vault_url + vault_relative_file + '"/>')
 
def export_figure(filename, new_filename, source_folder, destination_folder):
    import shutil
    _, source_ext = os.path.splitext(filename)
    new_basename, filter_ext = os.path.splitext(new_filename)
    if filter_ext and source_ext != filter_ext:
        return
    copy_from = os.path.join(source_folder, filename)
    copy_to = os.path.join(destination_folder, new_basename + source_ext)
    try:
        shutil.copy2(copy_from, copy_to)
        print(f"Figure exported as: {copy_to}")
    except IOError as e:
        print(f"Error exporting figure: {e}")
 
def delete_figure(folder_path):
    files_to_delete = ['figure.tex', 'figure.aux', 'figure.log', 'figure.out', 'figure.lot', 'figure_precrop.pdf', 'figure.pdf', 'figure.svg']
    for filename in files_to_delete:
        file_path = os.path.join(folder_path, filename)
        if os.path.exists(file_path):
            os.remove(file_path)
    os.rmdir(folder_path)
 
def generate_latex_figure(latex_source, compiler="pdflatex", escape_shell=False, outfile=None, keep_intermediates=False, rerun=0):
    assets_folder = os.path.join(@vault_path, 'attachments/')
    figure_folder = compile_and_crop_figure(latex_source, assets_folder, rerun=rerun)
    if outfile:
        export_figure('figure.svg', outfile, figure_folder, assets_folder)
        export_figure('figure.pdf', outfile, figure_folder, assets_folder)
    print_figure('figure.svg', figure_folder, '.temp')
    if not keep_intermediates:
        delete_figure(figure_folder)
 
generate_latex_figure(r"""
\documentclass{standalone}
\begin{document}
Hello World!
\end{document}
""")

Minimal example

\documentclass{standalone}
\begin{document}
Hello World!
\end{document}

Save figure as figure hello world.svg

\documentclass{standalone}
\begin{document}
Hello World!
\end{document}

figure hello world.svg

Rerun to get cross-references right

\documentclass{article} \pagestyle{empty}
\usepackage{mathtools}
\begin{document}
I use \eqref{eq:einstein} a lot.
\[
    E = mc^2 \tag{1}\label{eq:einstein}
\]
\end{document}

Regular python code

import uuid
print("figure_" + str(uuid.uuid4()))
@show(@vault_url + '/attachments/.temp.svg')
def check_file_for_patterns(file_path):
    import re
    patterns = [
        r"Rerun to get cross-references right",
        r"Rerun to get outlines right",
        r"Label\(s\) may have changed\. Rerun"
    ]
    
    with open(file_path, 'r') as file:
        content = file.read()
        
    for pattern in patterns:
        if re.search(pattern, content):
            return True
    
    return False
 
if check_file_for_patterns(r'C:\Users\anton\AppData\Local\Temp\tmpxnojtw_g\figure.log'):
    print("Rerun needed. Will compile again.")
else:
    print("No rerun needed. Compilation complete.")

Sources:

Related:

Tags:
TeX Live
pdf2svg
Python