mirror of
https://github.com/wassname/greater_tables_project.git
synced 2026-06-27 16:15:38 +08:00
3.1.0: gtcore2 -> core; svg2 -> svg; deleted older greater_tables
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
__version__ = '3.0.0'
|
||||
__version__ = '3.1.0'
|
||||
__project__ = 'greater_tables'
|
||||
__author__ = 'Stephen J Mildenhall'
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+257
-282
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+102
-157
@@ -1,188 +1,133 @@
|
||||
"""
|
||||
Create and display svg files from tikz tex tables.
|
||||
Create and display SVG files from TikZ pictures embedded in LaTeX.
|
||||
|
||||
Good for testing.
|
||||
Good for testing. Outputs are cached by hash. PDF→SVG uses pdf2svg.
|
||||
|
||||
From great2.blog
|
||||
GPT re-write of my old great2.blog code.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
import re
|
||||
import yaml
|
||||
from itertools import count
|
||||
from subprocess import Popen, PIPE
|
||||
from IPython.display import display, Markdown, SVG
|
||||
from pathlib import Path
|
||||
from subprocess import run, Popen, PIPE
|
||||
from IPython.display import SVG, display
|
||||
|
||||
from . hasher import txt_short_hash
|
||||
from .hasher import txt_short_hash
|
||||
|
||||
|
||||
class TikzProcessor():
|
||||
_tex_template_full = """\\documentclass[10pt, border=5mm]{{standalone}}
|
||||
|
||||
% needs lualatex - uncomment for Wiley fonts
|
||||
%\\usepackage{{fontspec}}
|
||||
%\\setmainfont{{Stix Two Text}}
|
||||
%\\usepackage{{unicode-math}}
|
||||
%\\setmathfont{{Stix Two Math}}
|
||||
|
||||
\\usepackage{{amsfonts}}
|
||||
\\usepackage{{url}}
|
||||
\\usepackage{{tikz}}
|
||||
\\usepackage{{color}}
|
||||
\\usetikzlibrary{{arrows,calc,positioning,shadows.blur,decorations.pathreplacing}}
|
||||
\\usetikzlibrary{{automata}}
|
||||
\\usetikzlibrary{{fit}}
|
||||
\\usetikzlibrary{{snakes}}
|
||||
\\usetikzlibrary{{intersections}}
|
||||
\\usetikzlibrary{{decorations.markings,decorations.text,decorations.pathmorphing,decorations.shapes}}
|
||||
\\usetikzlibrary{{decorations.fractals,decorations.footprints}}
|
||||
\\usetikzlibrary{{graphs}}
|
||||
\\usetikzlibrary{{matrix}}
|
||||
\\usetikzlibrary{{shapes.geometric}}
|
||||
\\usetikzlibrary{{mindmap, shadows}}
|
||||
\\usetikzlibrary{{backgrounds}}
|
||||
\\usetikzlibrary{{cd}}
|
||||
|
||||
% really common macros
|
||||
\\newcommand{{\\grtspacer}}{{\\vphantom{{lp}}}}
|
||||
|
||||
\\def\\dfrac{{\\displaystyle\\frac}}
|
||||
\\def\\dint{{\\displaystyle\\int}}
|
||||
|
||||
\\begin{{document}}
|
||||
|
||||
{tikz_begin}{tikz_code}{tikz_end}
|
||||
|
||||
\\end{{document}}
|
||||
"""
|
||||
# --------------------------------------------
|
||||
_tex_template = """
|
||||
% really common macros
|
||||
\\newcommand{{\\grtspacer}}{{\\vphantom{{lp}}}}
|
||||
|
||||
\\def\\dfrac{{\\displaystyle\\frac}}
|
||||
\\def\\dint{{\\displaystyle\\int}}
|
||||
|
||||
\\begin{{document}}
|
||||
|
||||
{tikz_begin}{tikz_code}{tikz_end}
|
||||
|
||||
\\end{{document}}
|
||||
class TikzProcessor:
|
||||
# Full TeX preamble to generate a .fmt if needed
|
||||
_tex_template_full = r"""\documentclass[10pt, border=5mm]{standalone}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{url}
|
||||
\usepackage{tikz}
|
||||
\usepackage{color}
|
||||
\usetikzlibrary{arrows,calc,positioning,shadows.blur,decorations.pathreplacing}
|
||||
\usetikzlibrary{automata,fit,snakes,intersections}
|
||||
\usetikzlibrary{decorations.markings,decorations.text,decorations.pathmorphing,decorations.shapes}
|
||||
\usetikzlibrary{decorations.fractals,decorations.footprints}
|
||||
\usetikzlibrary{graphs,matrix,shapes.geometric}
|
||||
\usetikzlibrary{mindmap,shadows,backgrounds,cd}
|
||||
\dump
|
||||
"""
|
||||
|
||||
def split_tikz(self):
|
||||
"""
|
||||
Split text to get the tikzpicture. Format is
|
||||
# Minimal template to embed user tikz
|
||||
_tex_template = r"""
|
||||
\newcommand{{\grtspacer}}{{\vphantom{{lp}}}}
|
||||
\def\dfrac{{\displaystyle\frac}}
|
||||
\def\dint{{\displaystyle\int}}
|
||||
\begin{{document}}
|
||||
{tikz_begin}{tikz_code}{tikz_end}
|
||||
\end{{document}}
|
||||
"""
|
||||
|
||||
initial text pip then groups of four:
|
||||
|
||||
1. begin tag ``(1::4)``
|
||||
2. tikz code ``(2::4)``
|
||||
3. end tag ``(3::4)``
|
||||
4. non-related text ``(4::4)``
|
||||
|
||||
"""
|
||||
return re.split(r'(\\begin{tikz(?:cd|picture)}|\\end{tikz(?:cd|picture)})', self.txt)
|
||||
|
||||
def __init__(self, txt, base_path='.', tex_engine='pdflatex'):
|
||||
"""
|
||||
TikzProcessor (from TikzConvertyer): process a tex tikz text string into svg.
|
||||
The program
|
||||
|
||||
* creates a pdf and svg from the tikz blob
|
||||
|
||||
lualatex is more robust, but slower...
|
||||
pdflatex can't handle the fancy wiley fonts
|
||||
|
||||
"""
|
||||
self.txt = txt
|
||||
self.tex_engine = tex_engine
|
||||
# directory for TeX and images
|
||||
self.base_path = Path(base_path).resolve()
|
||||
self.out_path = self.base_path / 'tikz'
|
||||
self.out_path.mkdir(exist_ok=True)
|
||||
self.file_path = self.out_path / txt_short_hash(txt)
|
||||
self.format_file = self.out_path / 'tikz_format.fmt'
|
||||
|
||||
def split_tikz(self):
|
||||
"""Split text to extract the TikZ picture."""
|
||||
return re.split(r'(\\begin{tikz(?:cd|picture)}|\\end{tikz(?:cd|picture)})', self.txt)
|
||||
|
||||
def ensure_format_file(self):
|
||||
"""Create format file for faster compilation if missing."""
|
||||
if self.format_file.exists():
|
||||
return
|
||||
print('building format file...')
|
||||
tmp = self.out_path / 'tikz_format.tex'
|
||||
tmp.write_text(self._tex_template_full, encoding='utf-8')
|
||||
self.run_command([
|
||||
'pdflatex',
|
||||
f'-ini',
|
||||
f'-jobname={self.format_file.stem}',
|
||||
'&pdflatex',
|
||||
tmp.name,
|
||||
], raise_on_error=True, cwd=self.out_path)
|
||||
# tmp.unlink()
|
||||
(self.out_path / f'{self.format_file.stem}.log').unlink()
|
||||
print('building format file...success', self.format_file.resolve())
|
||||
|
||||
def process_tikz(self, verbose=False):
|
||||
"""
|
||||
Process the tikz into pdf and svg
|
||||
"""
|
||||
# container contains a tikzpicture
|
||||
svg_path = self.file_path.with_suffix('.svg')
|
||||
tex_path = self.file_path.with_suffix('.tex')
|
||||
|
||||
# make tex code for a stand-alone document
|
||||
tikz_begin, tikz_code, tikz_end = self.split_tikz()[
|
||||
1:4]
|
||||
"""Compile TikZ to PDF and convert to SVG."""
|
||||
tikz_begin, tikz_code, tikz_end = self.split_tikz()[1:4]
|
||||
tex_code = self._tex_template.format(
|
||||
tikz_begin=tikz_begin, tikz_code=tikz_code, tikz_end=tikz_end)
|
||||
tikz_begin=tikz_begin,
|
||||
tikz_code=tikz_code,
|
||||
tikz_end=tikz_end
|
||||
)
|
||||
|
||||
tex_path = self.file_path.with_suffix('.tex')
|
||||
tex_path.write_text(tex_code, encoding='utf-8')
|
||||
print(
|
||||
f'TIKZ: created temp file = {tex_path.name}')
|
||||
pdf_file = tex_path.with_suffix('.pdf')
|
||||
print(f'TIKZ: Update pdf file')
|
||||
if self.tex_engine == 'pdflatex':
|
||||
# faster with template
|
||||
# TODO EVID hard coded template
|
||||
template_path = Path('tikz_format.fmt')
|
||||
assert template_path.exists()
|
||||
template = str(template_path)
|
||||
command = ['pdflatex', f'--fmt={template}',
|
||||
f'--output-directory={str(tex_path.parent.resolve())}',
|
||||
str(tex_path.resolve())]
|
||||
else:
|
||||
# for STIX fonts, no template
|
||||
command = ['lualatex',
|
||||
f'--output-directory={str(tex_path.parent.resolve())}',
|
||||
str(tex_path.resolve())]
|
||||
pdf_path = tex_path.with_suffix('.pdf')
|
||||
svg_path = tex_path.with_suffix('.svg')
|
||||
|
||||
self.ensure_format_file()
|
||||
|
||||
tex_cmd = [
|
||||
'pdflatex',
|
||||
f'--fmt={self.format_file.stem}',
|
||||
f'--output-directory={str(tex_path.parent)}',
|
||||
str(tex_path)
|
||||
]
|
||||
if verbose:
|
||||
print(f'TIKZ: TeX Command={" ".join(command)}')
|
||||
TikzProcessor.run_command(command)
|
||||
# to recreate
|
||||
(tex_path.parent /
|
||||
f'make_tikz.bat').write_text(" ".join(command))
|
||||
if verbose:
|
||||
print(
|
||||
f'TIKZ: Creating svg file for Tikz (using new pdf2svg util)')
|
||||
# https://github.com/jalios/pdf2svg-windows
|
||||
command = [
|
||||
print("Running:", " ".join(tex_cmd))
|
||||
self.run_command(tex_cmd)
|
||||
|
||||
(tex_path.parent / 'make_tikz.bat').write_text(" ".join(tex_cmd), encoding='utf-8')
|
||||
|
||||
svg_cmd = [
|
||||
'C:\\temp\\pdf2svg-windows\\dist-64bits\\pdf2svg',
|
||||
str(pdf_file.resolve()), str(svg_path.resolve())]
|
||||
# seems to return info on stderr?
|
||||
str(pdf_path),
|
||||
str(svg_path)
|
||||
]
|
||||
if verbose:
|
||||
print(f'PDF->SVG: {" ".join(command)}')
|
||||
TikzProcessor.run_command(command, flag=False)
|
||||
print("Running:", " ".join(svg_cmd))
|
||||
self.run_command(svg_cmd, raise_on_error=False)
|
||||
|
||||
if not verbose:
|
||||
# tidy up
|
||||
tex_path.unlink()
|
||||
tex_path.with_suffix('.aux').unlink()
|
||||
tex_path.with_suffix('.log').unlink()
|
||||
pdf_file.unlink()
|
||||
|
||||
@staticmethod
|
||||
def run_command(command, flag=True):
|
||||
"""
|
||||
Run a command and show results. Allows for weird xx behavior
|
||||
|
||||
:param command:
|
||||
:param flag:
|
||||
:return:
|
||||
"""
|
||||
with Popen(command, stdout=PIPE, stderr=PIPE, universal_newlines=True) as p:
|
||||
line1 = p.stdout.read()
|
||||
line2 = p.stderr.read()
|
||||
exit_code = p.poll()
|
||||
if line1:
|
||||
print('\n' + line1[-250:])
|
||||
if line2:
|
||||
if flag:
|
||||
raise ValueError(line2)
|
||||
else:
|
||||
print(line2)
|
||||
return exit_code
|
||||
for ext in ('.tex', '.aux', '.log', '.pdf'):
|
||||
path = tex_path.with_suffix(ext)
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
|
||||
def display(self):
|
||||
"""display in Jupyter Lab."""
|
||||
"""Display the SVG in Jupyter."""
|
||||
display(SVG(self.file_path.with_suffix('.svg')))
|
||||
|
||||
@staticmethod
|
||||
def run_command(command, raise_on_error=True, cwd=None):
|
||||
"""Run command with subprocess and show output."""
|
||||
with Popen(command, cwd=cwd, stdout=PIPE, stderr=PIPE, universal_newlines=True) as p:
|
||||
stdout, stderr = p.communicate()
|
||||
if stdout:
|
||||
print(stdout.strip()[-250:])
|
||||
if stderr:
|
||||
if raise_on_error:
|
||||
raise RuntimeError(stderr.strip())
|
||||
else:
|
||||
print(stderr.strip())
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
"""
|
||||
Create and display SVG files from TikZ pictures embedded in LaTeX.
|
||||
|
||||
Good for testing. Outputs are cached by hash. PDF→SVG uses pdf2svg.
|
||||
|
||||
GPT re-write of my old great2.blog code.
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from subprocess import run, Popen, PIPE
|
||||
from IPython.display import SVG, display
|
||||
|
||||
from .hasher import txt_short_hash
|
||||
|
||||
|
||||
class TikzProcessor:
|
||||
# Full TeX preamble to generate a .fmt if needed
|
||||
_tex_template_full = r"""\documentclass[10pt, border=5mm]{standalone}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{url}
|
||||
\usepackage{tikz}
|
||||
\usepackage{color}
|
||||
\usetikzlibrary{arrows,calc,positioning,shadows.blur,decorations.pathreplacing}
|
||||
\usetikzlibrary{automata,fit,snakes,intersections}
|
||||
\usetikzlibrary{decorations.markings,decorations.text,decorations.pathmorphing,decorations.shapes}
|
||||
\usetikzlibrary{decorations.fractals,decorations.footprints}
|
||||
\usetikzlibrary{graphs,matrix,shapes.geometric}
|
||||
\usetikzlibrary{mindmap,shadows,backgrounds,cd}
|
||||
\dump
|
||||
"""
|
||||
|
||||
# Minimal template to embed user tikz
|
||||
_tex_template = r"""
|
||||
\newcommand{{\grtspacer}}{{\vphantom{{lp}}}}
|
||||
\def\dfrac{{\displaystyle\frac}}
|
||||
\def\dint{{\displaystyle\int}}
|
||||
\begin{{document}}
|
||||
{tikz_begin}{tikz_code}{tikz_end}
|
||||
\end{{document}}
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, txt, base_path='.', tex_engine='pdflatex'):
|
||||
self.txt = txt
|
||||
self.tex_engine = tex_engine
|
||||
self.base_path = Path(base_path).resolve()
|
||||
self.out_path = self.base_path / 'tikz'
|
||||
self.out_path.mkdir(exist_ok=True)
|
||||
self.file_path = self.out_path / txt_short_hash(txt)
|
||||
self.format_file = self.out_path / 'tikz_format.fmt'
|
||||
|
||||
def split_tikz(self):
|
||||
"""Split text to extract the TikZ picture."""
|
||||
return re.split(r'(\\begin{tikz(?:cd|picture)}|\\end{tikz(?:cd|picture)})', self.txt)
|
||||
|
||||
def ensure_format_file(self):
|
||||
"""Create format file for faster compilation if missing."""
|
||||
if self.format_file.exists():
|
||||
return
|
||||
print('building format file...')
|
||||
tmp = self.out_path / 'tikz_format.tex'
|
||||
tmp.write_text(self._tex_template_full, encoding='utf-8')
|
||||
self.run_command([
|
||||
'pdflatex',
|
||||
f'-ini',
|
||||
f'-jobname={self.format_file.stem}',
|
||||
'&pdflatex',
|
||||
tmp.name,
|
||||
], raise_on_error=True, cwd=self.out_path)
|
||||
# tmp.unlink()
|
||||
(self.out_path / f'{self.format_file.stem}.log').unlink()
|
||||
print('building format file...success', self.format_file.resolve())
|
||||
|
||||
def process_tikz(self, verbose=False):
|
||||
"""Compile TikZ to PDF and convert to SVG."""
|
||||
tikz_begin, tikz_code, tikz_end = self.split_tikz()[1:4]
|
||||
tex_code = self._tex_template.format(
|
||||
tikz_begin=tikz_begin,
|
||||
tikz_code=tikz_code,
|
||||
tikz_end=tikz_end
|
||||
)
|
||||
|
||||
tex_path = self.file_path.with_suffix('.tex')
|
||||
tex_path.write_text(tex_code, encoding='utf-8')
|
||||
pdf_path = tex_path.with_suffix('.pdf')
|
||||
svg_path = tex_path.with_suffix('.svg')
|
||||
|
||||
self.ensure_format_file()
|
||||
|
||||
tex_cmd = [
|
||||
'pdflatex',
|
||||
f'--fmt={self.format_file.stem}',
|
||||
f'--output-directory={str(tex_path.parent)}',
|
||||
str(tex_path)
|
||||
]
|
||||
if verbose:
|
||||
print("Running:", " ".join(tex_cmd))
|
||||
self.run_command(tex_cmd)
|
||||
|
||||
(tex_path.parent / 'make_tikz.bat').write_text(" ".join(tex_cmd), encoding='utf-8')
|
||||
|
||||
svg_cmd = [
|
||||
'C:\\temp\\pdf2svg-windows\\dist-64bits\\pdf2svg',
|
||||
str(pdf_path),
|
||||
str(svg_path)
|
||||
]
|
||||
if verbose:
|
||||
print("Running:", " ".join(svg_cmd))
|
||||
self.run_command(svg_cmd, raise_on_error=False)
|
||||
|
||||
if not verbose:
|
||||
for ext in ('.tex', '.aux', '.log', '.pdf'):
|
||||
path = tex_path.with_suffix(ext)
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
|
||||
def display(self):
|
||||
"""Display the SVG in Jupyter."""
|
||||
display(SVG(self.file_path.with_suffix('.svg')))
|
||||
|
||||
@staticmethod
|
||||
def run_command(command, raise_on_error=True, cwd=None):
|
||||
"""Run command with subprocess and show output."""
|
||||
with Popen(command, cwd=cwd, stdout=PIPE, stderr=PIPE, universal_newlines=True) as p:
|
||||
stdout, stderr = p.communicate()
|
||||
if stdout:
|
||||
print(stdout.strip()[-250:])
|
||||
if stderr:
|
||||
if raise_on_error:
|
||||
raise RuntimeError(stderr.strip())
|
||||
else:
|
||||
print(stderr.strip())
|
||||
Reference in New Issue
Block a user