#! /usr/bin/env python ################################################ # Convert each figchild picture to a PDF file. # # # # In fact, this script creates only a Ninja # # build file to perform the conversion and a # # fakefigchild.sty file that uses the # # generated PDF files. # # # # Author: Scott Pakin # ################################################ import os import re import subprocess import sys import textwrap def kpsewhich(fname): 'Find a filename in the TeX tree.' proc = subprocess.run(['kpsewhich', fname], capture_output=True, check=False, encoding='utf-8') if proc.returncode != 0: return None return proc.stdout.strip() # If figchild.sty doesn't exist, write a dummy figchild.ninja and exit. sty = kpsewhich('figchild.sty') if sty is None: with open('figchild.ninja', 'w') as w: w.write('# This is a generated file. DO NOT EDIT.\n') w.write('# Edit %s instead.\n' % os.path.abspath(sys.argv[0])) w.write(''' rule touch command = touch $out build fakefigchild.sty : touch build FIGCHILD : phony fakefigchild.sty ''') sys.exit(0) # Extract all commands defined by figchild.sty and the package description. desc = None commands = [] newcmd_re = re.compile(r'^\s*\\newcommand{\\(fc\w+)\}') desc_re = re.compile(r'\\ProvidesPackage\{figchild\}\[(.*?)\]') with open(sty) as r: for ln in r: # Extract the package description. if desc is None: match = desc_re.search(ln) if match is not None: desc = match[1] # Extract a command. match = newcmd_re.search(ln) if match is None: continue if match[1] != 'fcAlligatorB': # Undocumented and broken commands.append(match[1]) commands.sort(key=lambda s: s.lower()) # Generate a Ninja build file for managing PDF creation. with open('figchild.ninja', 'w') as w: # Write some boilerplate code. w.write('# This is a generated file. DO NOT EDIT.\n') w.write('# Edit %s instead.\n' % os.path.abspath(sys.argv[0])) w.write(r''' rule write-file command = /bin/echo -e '$body' > $out description = Creating $out build figchild/preloaded.tex : write-file body = $ \\documentclass{minimal}\n$ \\usepackage[a2paper,landscape]{geometry}\n$ \\usepackage{figchild}\n$ \\usepackage{graphicx}\n$ \\begin{document}\n$ \\end{document} rule generate_mylatex command = $ cd figchild ; $ pdflatex --ini '&pdflatex' mylatex.ltx preloaded.tex description = Creating mylatex.fmt for faster builds ''') w.write('build figchild/mylatex.fmt figchild/mylatex.log :' ' generate_mylatex figchild/preloaded.tex | %s %s\n' % (kpsewhich('mylatex.ltx'), kpsewhich('figchild.sty'))) w.write(r''' rule write_symbol_tex command = bash -c '$ sym=$$(basename $out .tex) ; $ echo -e "$ \\\\documentclass{minimal}\\n$ \\\\begin{document}\\n$ \\\\resizebox{!}{48pt}{\\\\$$sym[line width=3]}\\n$ \\\\end{document}" > $out' description = Writing $out rule write_wide_short_symbol_tex command = bash -c '$ sym=$$(basename $out .tex) ; $ echo -e "$ \\\\documentclass{minimal}\\n$ \\\\begin{document}\\n$ \\\\resizebox{!}{12pt}{\\\\$$sym[line width=3]}\\n$ \\\\end{document}" > $out' description = Writing $out ''') # For each symbol defined by figchild.sty, create a LaTeX file. for sym in commands: if sym == 'fcPencil': # Special case for \fcPencil, which is abnormally wide and short w.write(f'build figchild/{sym}.tex : write_wide_short_symbol_tex\n') else: # Common case w.write(f'build figchild/{sym}.tex : write_symbol_tex\n') # Compile each LaTeX file to a PDF file. w.write(r''' rule tex_to_pdf command = $ texname="$$(basename $in)" ; $ jname="$$(basename $in .tex)-uncrop" ; $ cd figchild ; $ pdflatex -jobname "$$jname" '&mylatex' "$$texname" description = pdflatex $in ''') for sym in commands: w.write(f'build figchild/{sym}-uncrop.pdf figchild/{sym}-uncrop.log figchild/{sym}-uncrop.aux : tex_to_pdf figchild/{sym}.tex | figchild/mylatex.fmt\n') w.write('\n') # Crop each PDF file. w.write('rule crop-pdf\n') w.write(' command = pdfcrop $in $out\n') w.write(' description = Cropping $in to produce $out\n\n') for sym in commands: w.write(f'build figchild/{sym}.pdf : crop-pdf figchild/{sym}-uncrop.pdf\n') # Create a fakefigchild.sty file. w.write(r''' build fakefigchild.sty : write-file body = $ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n$ % This is a generated file. DO NOT EDIT. %\n$ % Edit makefakefigchild instead. %\n$ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n$ \n$''') w.write(r''' \\NeedsTeXFormat{LaTeX2e}\n$ \\ProvidesPackage{fakefigchild}[%s]\n$ \\RequirePackage{graphicx}\n$ \n$ ''' % desc) for cmd in commands: ht = 20 if cmd == 'fcPencil': # Special case for \fcPencil, which is abnormally wide and short. ht = 5 w.write(' \\\\DeclareRobustCommand{\\\\%s}{%%\\n$\n' % cmd) w.write(' $ $ \\\\includegraphics[height=%dpt,keepaspectratio]' '{figchild/%s}%%\\n$\n' % (ht, cmd)) w.write(' }\\n$\n') w.write(' \\n$\n') w.write(' \\\\endinput\n') w.write('\n') # Create a phony symbol that depends on fakefigchild.sty and all # generated PDF files. w.write('build FIGCHILD : phony $\n') phony = [f'figchild/{sym}.pdf' for sym in commands] + ['fakefigchild.sty'] lines = textwrap.wrap(' '.join(phony), break_long_words=False, break_on_hyphens=False, initial_indent=' ', subsequent_indent=' ') w.write('%s\n' % ' $\n'.join(lines))