#! /usr/bin/env python ############################################## # Extract each lucide-icons symbol to a # # separate PDF file. # # # # In fact, this script creates only a Ninja # # build file to perform the operation. # # # # Author: Scott Pakin # ############################################## import glob 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() def header_string(): 'Return a "generated file" header string.' full_name = os.path.abspath(sys.argv[0]) gen_line = 'This is a generated file. DO NOT EDIT.' edit_line = f'Edit {full_name} instead.' max_chars = max(len(gen_line), len(edit_line)) hash_line = '#' * (max_chars + 4) return '\n'.join([ hash_line, '# %-*.*s #' % (max_chars, max_chars, gen_line), '# %-*.*s #' % (max_chars, max_chars, edit_line), hash_line, '', ]) def parse_sty_file(source_sty): '''Parse lucide-icons.sty into a dictionary mapping page number to icon name.''' num2name = {} generate_re = re.compile(r'^\\GenerateLucideIcons\{(\d+)\}\{([^\}]+)\}') with open(source_sty) as r: for ln in r: match = generate_re.match(ln) if match is None: continue num2name[int(match[1])] = match[2] return num2name # Generate a Ninja file that extracts each page of lucide-icons-all.pdf into a # separate file. with open('lucide-icons.ninja', 'w') as w: # Write the header string then stop if lucide-icons-all.pdf is unavailable. w.write(header_string()) source_pdf = kpsewhich('lucide-icons-all.pdf') if source_pdf is None: w.write('') w.write('build LUCIDE-ICONS : phony\n') sys.exit(0) # Define a rule that converts lucide-icons.sty to fakelucide-icons.sty. source_sty = kpsewhich('lucide-icons.sty') if source_sty is None: raise FileNotFoundError('No such file lucide-icons.sty') full_name = os.path.abspath(sys.argv[0]) w.write(rf''' rule fake-lucide-icons-sty command = $ echo '%% This is a generated file. DO NOT EDIT.' > $out ; $ echo '%% Edit {full_name} instead.' >> $out ; $ echo '' >> $out ; $''') w.write(r''' /bin/echo '\DeclareRobustCommand*{\lucideicon}[1]{%' >> $out ; $ /bin/echo ' \raisebox{-2pt}{%' >> $out ; $ /bin/echo ' \includegraphics[height=10pt]{lucide-icons/#1}%' >> $out ; $ /bin/echo ' }%' >> $out ; $ /bin/echo '}%' >> $out ; $ /bin/echo '' >> $out ; $ /bin/echo '\endinput' >> $out description = Generate $out from lucide-icons.sty ''') # Invoke the preceding rule. w.write(f'build fakelucide-icons.sty : fake-lucide-icons-sty {source_sty}\n') # Define a rule that extracts a single page of lucide-icons-all.pdf into a # separate file. w.write(f''' rule extract-pdf-page command = pdfseparate -f $page -l $page $in 'lucide-icons/${{name}}.pdf' description = Extracting page $page from lucide-icons-all.pdf ''') # Invoke the preceding rule once for each page of lucide-icons-all.pdf. num_to_name = parse_sty_file(source_sty) for num, name in sorted(num_to_name.items()): w.write(f'build lucide-icons/{name}.pdf : extract-pdf-page {source_pdf}\n') w.write(f' page = {num}\n') w.write(f' name = {name}\n') w.write('\n') # Define a phony rule that depends on fakelucide-icons.sty and all of the # output files. w.write('build LUCIDE-ICONS : phony $\n') phony = [f'lucide-icons/{name}.pdf' for name in sorted(num_to_name.values())] + \ ['fakelucide-icons.sty'] lines = textwrap.wrap(' '.join(phony), width=77, break_long_words=False, break_on_hyphens=False, initial_indent=' ', subsequent_indent=' ') w.write('%s\n' % ' $\n'.join(lines))