From c45a0e15a3f74b963e3c2be142de33c8c8f12588 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 4 Jan 2024 03:02:56 +0000 Subject: CTAN sync 202401040302 --- info/symbols/comprehensive/source/makefakelualatex | 238 +++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100755 info/symbols/comprehensive/source/makefakelualatex (limited to 'info/symbols/comprehensive/source/makefakelualatex') diff --git a/info/symbols/comprehensive/source/makefakelualatex b/info/symbols/comprehensive/source/makefakelualatex new file mode 100755 index 0000000000..22a62799e1 --- /dev/null +++ b/info/symbols/comprehensive/source/makefakelualatex @@ -0,0 +1,238 @@ +#! /usr/bin/env python + +############################################## +# Convert LuaLaTeX symbol packages to a # +# faked version that works with pdfLaTeX. # +# # +# By Scott Pakin # +############################################## + +import argparse +import os +import re +import string +import subprocess +import sys + +# Define a "do not edit" header string. +do_not_edit = '''\ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% This is a generated file. DO NOT EDIT. % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +''' + + +def kpsewhich(fname): + 'Find a filename in the TeX tree.' + proc = subprocess.run(['kpsewhich', fname], capture_output=True, + check=True, encoding='utf-8') + return proc.stdout.strip() + + +class ParsingInfo(): + '''Represent the information needed to parse one type of symbol from + the input LaTeX package. Information is provided as a + semicolon-separated tuple {regexp, symbol suffix, hex increment}.''' + + def __init__(self, info): + fields = info.split(' ; ') + if len(fields) == 0: + raise RuntimeError('failed to parse %s' % repr(info)) + self.regexp = re.compile(fields[0], re.DOTALL) + self.suffix = '' + if len(fields) > 1: + self.suffix = fields[1] + self.increment = 0 + if len(fields) > 2: + self.increment = int(fields[2], 0) + + +class SubFont(): + 'Construct a sub-font containing up to 256 characters.' + + def __init__(self, name, ttf, sym_num): + self.name = name + self.ttf = ttf + self.sym_num = sym_num + + def _gen_encoding_file(self): + 'Generate an input encoding file.' + enc_in = 'fake' + self.name + '-in.enc' + with open(enc_in, 'w') as w: + w.write(do_not_edit) + w.write(f'/{self.name} [\n') + for i, (sym, num) in enumerate(self.sym_num): + w.write(' /uni%04X %% %3d: \\%s\n' % (num, i, sym)) + w.write(']\n') + return enc_in + + def _gen_font_files(self, enc_in): + 'Generate a .tfm, .map, and .enc file from an input .enc file.' + args = ['otftotfm', + f'--map-file=fake{self.name}.map', + '--no-updmap', + f'--encoding={enc_in}', + '--directory=' + os.getcwd(), + ttf, + f'fake{self.name}'] + sys.stderr.write('RUNNING: %s\n' % ' '.join(args)) + subprocess.run(args, check=True, encoding='utf-8') + + def _find_encoding_name(self): + 'Return the name of the generated encoding file.' + # Read the .map file to find the encoding name. + map = 'fake' + self.name + '.map' + with open(map) as r: + for ln in r: + toks = ln.split() + if len(toks) == 6: + return toks[4][2:] + raise Exception(f'failed to parse {map}') + + def _rename_encoding(self, enc): + 'Rename the encoding file and its reference in the map file.' + new_enc = 'fake' + self.name + '.enc' + os.rename(enc, new_enc) + map = 'fake' + self.name + '.map' + with open(map) as r: + all_map = r.readlines() + with open(map, 'w') as w: + for ln in all_map: + ln = ln.replace(enc, new_enc) + w.write(ln) + + def generate(self): + enc_in = self._gen_encoding_file() + self._gen_font_files(enc_in) + enc = self._find_encoding_name() + self._rename_encoding(enc) + os.remove(enc_in) + + +class LaTeXPackage(): + 'Generate a LaTeX package that provides access to multiple font files.' + + def __init__(self, sty, base, names, sym_num): + self.sty = sty + self.base = base + self.names = names + self.sym_num = sym_num + + def _provides_description(self): + '''Return the optional argument of the original package's + ProvidesPackage line.''' + with open(self.sty) as r: + all_sty = r.read() + provides_re = re.compile(r'\\ProvidesPackage\s*\{.*?\}\s*\[(.*?)\]', + re.DOTALL) + return provides_re.search(all_sty).group(1) + + def _map_lines(self): + 'Return the contents of all .map files.' + map_lines = [] + for name in self.names: + with open(name + '.map') as r: + for ln in r: + fields = ln.split() + if len(fields) == 6: + map_lines.append(ln.strip()) + return map_lines + + def generate(self): + 'Generate a .sty file.' + with open(f'fake{self.base}.sty', 'w') as w: + # Write a header block. + w.write(do_not_edit) + w.write('\\NeedsTeXFormat{LaTeX2e}\n') + desc = self._provides_description() + w.write('\\ProvidesPackage{fake%s}[%s]\n' % (self.base, desc)) + w.write('\n') + + # Inline all map-file contents. + for name, ln in zip(self.names, self._map_lines()): + w.write('\\pdfmapline{=%s}\n' % ln) + w.write('\\font\\%s=%s at 10pt\n' % (name, name)) + w.write('\n') + + # Define all symbols. + idx = 0 # Index into self.names + ofs = 0 # Offset into the 256-character font + for sym, num in self.sym_num: + w.write('\\DeclareRobustCommand*{\\%s}{{\\%s\\char"%02X}}' + ' %% "%04X\n' % + (sym, self.names[idx], ofs, num)) + ofs += 1 + if ofs == 256: + ofs = 0 + idx += 1 + w.write('\n') + + # Write a trailer line. + w.write('\\endinput\n') + + +########################################################################### + +# Parse the command line. +parser = argparse.ArgumentParser(description='Generate a pdfLaTeX-compatible' + ' interface to a LuaLaTeX package.') +parser.add_argument('base', + help='base name for files to read and write') +parser.add_argument('in_font', + help='name of underlying .ttf or .otf font file') +parser.add_argument('--regexp', action='append', + help='regular expression for extracting a LaTeX symbol' + ' name (no slash) as "sym" and a hexadecimal code (no' + ' prefix) as "hex". Optionally followed by " ; " and' + ' a symbol suffix and " ; " and a code increment') +cl_args = parser.parse_args() +base = cl_args.base +in_font = cl_args.in_font +if cl_args.regexp is None: + sym_hex_parsers = [ParsingInfo(r'\\symbol\{\"(?P[0-9A-F]+)\}.*?' + r'\\def\\(?P[A-Za-z]+)')] +else: + sym_hex_parsers = [ParsingInfo(regexp) for regexp in cl_args.regexp] + +# Find the original package and font files. +sty = kpsewhich(base + '.sty') +ttf = kpsewhich(in_font) # Misnomer; may be either TTF or OTF. + +# Read the entire package file into a single string. +with open(sty) as r: + all_sty = r.read() + +# Construct a list of {symbol name, Unicode number} pairs. +sym_num = [] +for shp in sym_hex_parsers: + for match in shp.regexp.finditer(all_sty): + sym, hex = match.group('sym'), match.group('hex') + sym_num.append((sym + shp.suffix, int(hex, 16) + shp.increment)) +if sym_num == []: + sys.exit('No {symbol, hex} pairs found') + +# Generate sub-fonts. +if len(sym_num) <= 256: + # Single required font file + subfont = SubFont(base, ttf, sym_num) + subfont.generate() + names = ['fake' + base] +else: + # Two or more required font files + names = [] + sub_sym_num = sym_num + while sub_sym_num != []: + suffix = string.ascii_uppercase[len(names)] + names.append('fake' + base + suffix) + subfont = SubFont(base + suffix, ttf, sub_sym_num[:256]) + subfont.generate() + sub_sym_num = sub_sym_num[256:] + +# Generate a LaTeX package. +package = LaTeXPackage(sty, base, names, sym_num) +package.generate() + +# Delete all map files now that they've been inlined. +for name in names: + os.remove(name + '.map') -- cgit v1.2.3