3.1.0: gtcore2 -> core; svg2 -> svg; deleted older greater_tables

This commit is contained in:
Stephen Mildenhall
2025-06-14 13:34:09 +01:00
parent 863ee43b43
commit 51b2d2bc7d
6 changed files with 360 additions and 6193 deletions
+1 -1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+102 -157
View File
@@ -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())
-133
View File
@@ -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())