#!/usr/bin/env python # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # % # This file is part of the 'lilyglyphs' LaTeX package. % # ========== % # % # https://github.com/openlilylib/lilyglyphs % # http://www.openlilylib.org/lilyglyphs % # % # Copyright 2012-2020 Urs Liska and others, ul@openlilylib.org % # % # 'lilyglyphs' is free software: you can redistribute it and/or modify % # it under the terms of the LaTeX Project Public License, either % # version 1.3 of this license or (at your option) any later version. % # You may find the latest version of this license at % # http://www.latex-project.org/lppl.txt % # more information on % # http://latex-project.org/lppl/ % # and version 1.3 or later is part of all distributions of LaTeX % # version 2005/12/01 or later. % # % # This work has the LPPL maintenance status 'maintained'. % # The Current Maintainer of this work is Urs Liska (see above). % # % # This work consists of the files listed in the file 'manifest.txt' % # which can be found in the 'license' directory. % # % # This program is distributed in the hope that it will be useful, % # but WITHOUT ANY WARRANTY; without even the implied warranty of % # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % # % # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% import os, sys, getopt, datetime, subprocess, argparse, lilyglyphs_common as lg # ############### # string to be printed before the actual command lily_src_prefix = """\\version "2.16.2" #(set-global-staff-size 14) \paper { indent = 0 } \header { tagline = "" } """ # string to be printed after the actual command definition lily_src_score = """ \\score { \\new Staff \\with { \\remove "Staff_symbol_engraver" \\remove "Clef_engraver" \\remove "Time_signature_engraver" } """ def main(): """Do the actual work of the script""" print('') print('buildglyphimages.py,') print('Part of lilyglyphs.') print('') # set CWD and ensure the necessary subdirs are present check_paths() print('') # load and parse input file lg.read_input_file(in_file) read_entries() print('') # generate LilyPond source files for each command # and compile them write_lily_src_files() print('') lg.compile_lily_files() print('') # remove intermediate files and move pdfs to pdf directory lg.cleanup_lily_files() print('') # generate latex commands and example code # and write them to the output file lg.generate_latex_commands() print('') write_latex_file() def check_paths(): """Sets CWD to 'glyphimages' subdir or root of lilyglyphs_private and makes sure that the necessary subdirectories are present""" # one level above 'definitions' is 'glyphimages' os.chdir(os.path.join(in_path, '..')) # check the presence of the necessary subdirectories # and create them if necessary # (otherwise we'll get errors when trying to write in them) ls = os.listdir('.') if not lg.dir_lysrc in ls: os.mkdir(lg.dir_lysrc) if not lg.dir_pdfs in ls: os.mkdir(lg.dir_pdfs) if not lg.dir_cmd in ls: os.mkdir(lg.dir_cmd) def cmd_filename(cmd_name): if cmd_name.startswith('lily'): return cmd_name[:4] + '-' + cmd_name[4:] elif cmd_name.startswith('lily-'): return cmd_name else: return 'lily-' + cmd_name # set default scale and raise arguments to empty scale = '' rais = '' def read_entries(): """Parses the input source file and extracts glyph entries""" print('Read entries of LilyPond commands:') for i in range(len(lg.definitions_file)): if '%%lilyglyphs' in lg.definitions_file[i]: i = read_entry(i) print(lg.lily_files) def read_entry(i): """Reads a single glyph entry from the input file and stores it in the global dictionary lg.in_cmds""" global scale, rais # read comment line(s) i += 1 is_protected = False comment = [] # loop over the comment line(s) while i < len(lg.definitions_file): line = lg.definitions_file[i].strip() # check for 'protected' entries that shouldn't be processed newly if not line[0] == '%': break elif '%%protected' in line: is_protected = True elif 'scale=' in line: dummy, scale = line.split('=') elif 'raise=' in line: dummy, rais = line.split('=') else: line = line[1:].strip() comment.append(line) i += 1 # remove any empty lines while len(lg.definitions_file[i].strip()) == 0: i += 1 # read command name line = lg.definitions_file[i].strip() cmd_name = line[: line.find('=') - 1] print('- ' + cmd_name, end=' ') if is_protected: print('(protected and skipped)') else: print('') #(for line break only) # read actual command until we find a line the begins with a closing curly bracket i += 1 lilySrc = [] while lg.definitions_file[i][0] != '}': lilySrc.append(lg.definitions_file[i]) i += 1 if not is_protected: lg.in_cmds[cmd_name] = {} lg.in_cmds[cmd_name]['comment'] = comment lg.in_cmds[cmd_name]['lilySrc'] = lilySrc lg.in_cmds[cmd_name]['element'] = cmd_filename(cmd_name) lg.in_cmds[cmd_name]['type'] = 'image' if scale: lg.in_cmds[cmd_name]['scale'] = scale if rais: lg.in_cmds[cmd_name]['raise'] = rais lg.lily_files.append(cmd_filename(cmd_name)) return i def usage(): print("""buildglyphimages. Part of the lilyglyphs package. Parses a template file, creates single .ly files from it, uses LilyPond to create single glyph pdf files and set up template files to be used in LaTeX. The input file has to be located in the glyphimages folder of either the lilyglyph package itself or of a copy of the lilyglyphs_private directory structure contained in the distribution. For detailed instructions refer to the manual. Usage: buildglyphimages.py in-file-name. """) def write_file_info(name, fout): """Formats file specific information for the lilyPond source file""" long_line = '% This file defines a single glyph to be created with LilyPond: %\n' width = len(long_line) - 1 header = '%' * width + '\n' spacer = '%' + ' ' * (width - 2) + '%\n' padding = width - len(name) - 8 fout.write(header) fout.write(spacer) fout.write(long_line) fout.write(spacer) fout.write('% ' + name + '.ly' + ' ' * padding + '%\n') fout.write(spacer) fout.write(header) fout.write(lg.signature()) fout.write('\n\n') def write_latex_file(): """Composes LaTeX file and writes it to disk""" print('Generate LaTeX file') print(lg.dir_cmd, in_basename) lg.write_latex_file(os.path.join(os.getcwd(), lg.dir_cmd, in_basename + '.tex')) def write_lily_src_files(): """Generates one .ly file for each found new command""" skip_cmds = [] print('Write .ly files for each entry:') for cmd_name in lg.in_cmds: print('- ' + cmd_name) gen_src_name = os.path.join(lg.dir_lysrc, cmd_filename(cmd_name) + '.ly') # handle existing commands if os.path.exists(gen_src_name): action = '' while not (action == 'Y' or action == 'N'): action = input('already present. Overwrite (y/n)? ') action = action.upper() if action == 'N': skip_cmds.append(cmd_name) continue # open a single lily src file for write access fout = open(gen_src_name, 'w') #output the license information fout.write(lg.lilyglyphs_copyright_string) fout.write('\n') #output information on the actual file write_file_info(cmd_name, fout) #write the default LilyPond stuff fout.write(lily_src_prefix) # write the comment for the command fout.write('%{\n') for line in lg.in_cmds[cmd_name]['comment']: fout.write(line + '\n') fout.write('%}\n\n') # write the actual command fout.write(cmd_name + ' = {\n') for line in lg.in_cmds[cmd_name]['lilySrc']: fout.write(line + '\n') fout.write('}\n') # write the score definition fout.write(lily_src_score) # finish the LilyPond file fout.write(' \\' + cmd_name + '\n') fout.write('}\n\n') fout.close() # remove skipped commands from in_cmds print(skip_cmds) for cmd_name in skip_cmds: del lg.in_cmds[cmd_name] lg.lily_files.remove(cmd_filename(cmd_name)) # #################################### # Finally launch the program if __name__ == "__main__": # parse command line arguments parser = argparse.ArgumentParser( description='Process templates to LilyPond input files,' + 'compile these and generate LaTeX commands.', parents=[lg.common_arguments]) parser.add_argument('i', type=lg.is_file, metavar='INPUT', help='File with command templates.') args = parser.parse_args() # if we have exactly one existing file # join its components and run the program # process filename argument, providing absolute path script_path, script_name = os.path.split(sys.argv[0]) in_file = os.path.join(os.getcwd(), vars(args)['i']) # check if the input file is in the right place # this has to be the definitions subdir of # the package directory or the lilyglyphs_private dir in_path, in_filename = os.path.split(in_file) in_path = os.path.normpath(in_path) if not (('lilyglyphs' in in_path) and (in_path.endswith('definitions'))): print('File in the wrong location: ' + in_path) usage() sys.exit(2) in_basename, in_ext = os.path.splitext(in_filename) main()