from flask import Flask, Response, request, render_template import subprocess import os import re import sys import tempfile import io import requests from svg_to_hpgl import svgToHPGL app = Flask(__name__) title = 'Cobbled paths' fonts_directory = 'db/' possible_extensions = [".flf"] etherpad = 'https://pad.constantvzw.org/p/' prefix = 'cobbled-pad-' # VARIABLES 4 CATALOGUE # ------------------------------ output = { 'stroke': { 'ascii': ' | ' , 'fonts': [] }, 'script': { 'ascii': ' _/' , 'fonts': [] }, 'block': { 'ascii': '|_|' , 'fonts': [] }, 'outline': { 'ascii': '/ /' , 'fonts': [] }, 'effect': { 'ascii': ': :' , 'fonts': [] }, 'pattern': { 'ascii': ')()' , 'fonts': [] }, # 'fill': { 'ascii': '_/', 'fonts': {} }, # 'directions': { 'ascii': '_/', 'fonts': {} }, # '3d': { 'ascii': '_/', 'fonts': {} }, # 'frame': { 'ascii': '_/', 'fonts': {} }, # 'code': { 'ascii': '_/', 'fonts': {} }, } databases = { 'default': 'fonts made by the figlet developpers and given with the program, early 1993', 'contributed': 'fonts made by figlet amateurs and submitted to the official figlet ftp, from before 1993 to 2005', 'jave': 'figlet font library of JavE (a free Ascii drawing Editor)', } # VARIABLES 4 REGEX # ------------------------------ # all the character that svgbob understand spec = [".", ",", "’", "'", "`", "+", "*", "o", "O", "V", "\\-", "|", "~", "_", ":", "!", "<", ">", "v", "^", "/", "\\\\", '\\”', '\\"', "(", ")", "=", "#"] r_spec = "".join(spec) r_nspec = "[^" + r_spec + "\€\$\s]" # autofix regex autofix = [ # every arrowshead into lines [re.compile("[<{]"), "("], [re.compile("[>}L]"), ")"], [re.compile("[vV]"), "-"], [re.compile("[\\^]"), "-"], [";", ":"], ["7", "/"], [re.compile("[1Tlj\\[\\]]"), "|"], [re.compile("[Y]"), "+"], # every not in the spec --> block [re.compile(r_nspec), "#"], ] # FUNCTIONS # ------------------------------ def most_common(lst): return max(set(lst), key=lst.count) def text2figlet(input, figfont): figlet = subprocess.run(["figlet", input, "-f", figfont, "-w", "160"], stdout = subprocess.PIPE, text=True) return figlet.stdout def ascii2svg(ascii_input, weight): svgbob = subprocess.run(["svgbob_cli", '--stroke-width', weight], input = ascii_input, stdout = subprocess.PIPE, text=True) return svgbob.stdout def ascii_autofix(ascii): for regex, replace in autofix: ascii = re.sub(regex, replace, ascii) return ascii def autofix_indication(ascii): for regex, replace in autofix: # the two markers have to not appear in any regex ascii = re.sub(regex, "$" + replace + "€", ascii) ascii = re.sub("[\$]", "", ascii) ascii = re.sub("[\€]", "", ascii) return ascii # ROUTES # ------------------------------ # _ _ # (_)_ __ __| | _____ __ # | | '_ \ / _` |/ _ \ \/ / # | | | | | (_| | __/> < # |_|_| |_|\__,_|\___/_/\_\ # # PRESENT THE TOOL @app.route("/") def index(): return render_template( 'index.html', title = title) # _ # __| |_ __ __ ___ __ # / _` | '__/ _` \ \ /\ / / # | (_| | | | (_| |\ V V / # \__,_|_| \__,_| \_/\_/ # # ETHERPAD 2 SVGBOB INTERFACE # one iframe for the etherpad # another iframe to dump the generated svg @app.route("/draw.html") def draw(): params = { 'pad': request.args.get('p') or 'default', 'weight': request.args.get('w') or '3', } params['pad-full'] = etherpad + prefix + params['pad'] return render_template( 'draw.html', title = title, params = params) # this is the route of the iframe where the svg is generated and dumped @app.route("/drawing/") def drawing(id): params = { 'pad': id or 'default', 'weight': request.args.get('w') or '3', } params['pad-full'] = etherpad + prefix + params['pad'] # get pad content print(' getting ' + params['pad-full']) pad_export = requests.get(params['pad-full'] + '/export/txt') ascii_input = pad_export.text # to SVG svg = ascii2svg(ascii_input, params['weight']) return render_template( 'drawing.html', title = title, params = params, svg = svg) # __ _ _ _ # / _| ___ _ __ | |_ _ __ ___ __ _| | _(_)_ __ __ _ # | |_ / _ \| '_ \| __| | '_ ` _ \ / _` | |/ / | '_ \ / _` | # | _| (_) | | | | |_ | | | | | | (_| | <| | | | | (_| | # |_| \___/|_| |_|\__| |_| |_| |_|\__,_|_|\_\_|_| |_|\__, | # |___/ # # EDITING A FIGGONT ON A PAD TO THEN USE IT @app.route("/font.html") def font(): params = { 'text': request.args.get('t') or 'Cobbled Paths', 'pad': request.args.get('p') or 'standard', 'weight': request.args.get('w') or '3', } params['pad-full'] = etherpad + prefix + params['pad'] return render_template( 'font.html', title = title, params = params) @app.route("/writing/") def writing(id): params = { 'text': request.args.get('t') or 'Cobbled Paths', 'pad': id or 'standard', 'weight': request.args.get('w') or '3', } params['pad-full'] = etherpad + prefix + params['pad'] # get pad content print(' getting ' + params['pad-full']) pad_export = requests.get(params['pad-full'] + '/export/txt') ascii_input = pad_export.text # store as a temporary file print('--- saving figfont as temp ---') (figfont_file, figfont_path) = tempfile.mkstemp(suffix='.flf') print(figfont_path) with open(figfont_path, 'w') as figfont_file: figfont_file.write(ascii_input) print('--- opening the figfont ---') f = {} f['ascii'] = text2figlet(params['text'], figfont_path) print(f['ascii']) print('--- rendering to svg ---') svg = ascii2svg(f['ascii'], params['weight']) return render_template( 'drawing.html', title = title, params = params, svg = svg) # _ _ # ___ __ _| |_ __ _| | ___ __ _ _ _ ___ # / __/ _` | __/ _` | |/ _ \ / _` | | | |/ _ \ # | (_| (_| | || (_| | | (_) | (_| | |_| | __/ # \___\__,_|\__\__,_|_|\___/ \__, |\__,_|\___| # |___/ # # FIGLET 2 SVGBOB INTERACTIVE CATALOGUE @app.route("/catalogue.html") def catalogue(): # text and weight as get parameter params = { 'text': request.args.get('t') or 'Echoes', 'weight': request.args.get('w') or '3', } # walk in the figlet font directory for root, dirs, files in os.walk(fonts_directory): for name in files: (basename, ext) = os.path.splitext(name) if ext in possible_extensions: figfont = os.path.join(root, name) print(figfont) # get font category out of last folder catalogue = root.split('/')[-2] type = root.split('/')[-1] if type in output: f = {} output[type]['fonts'].append(f) f['name'] = name f['catalogue'] = catalogue f['ascii'] = text2figlet(params['text'], figfont) f['svg'] = ascii2svg(f['ascii'], params['weight']) # regex auto_fix f['ascii_fix'] = ascii_autofix(f['ascii']) if f['ascii'] != f['ascii_fix']: f['autofix'] = True f['ascii_fix_indication'] = autofix_indication(f['ascii_fix']) f['svg_fix'] = ascii2svg(f['ascii_fix'], params['weight']) return render_template( 'catalogue.html', title = title, databases = databases, output = output, params = params) # _ _ _ # | |__ _ __ __ _| | _____ ___ __ ___ _ __| |_ # | '_ \| '_ \ / _` | | / _ \ \/ / '_ \ / _ \| '__| __| # | | | | |_) | (_| | | | __/> <| |_) | (_) | | | |_ # |_| |_| .__/ \__, |_| \___/_/\_\ .__/ \___/|_| \__| # |_| |___/ |_| # # FIGLET 2 SVGBOB INTERACTIVE CATALOGUE @app.route('/hpgl/') def hpgl (id): params = { 'pad': id or 'default', 'weight': request.args.get('w') or '3', } params['pad-full'] = etherpad + prefix + params['pad'] # get pad content print(' getting ' + params['pad-full']) pad_export = requests.get(params['pad-full'] + '/export/txt') ascii_input = pad_export.text # to SVG svg = ascii2svg(ascii_input, params['weight']) svg = re.sub(r'\\<\/rect\>', '', svg, flags=re.M) #print(svg) # store as a temporary file (svg_file, svg_path) = tempfile.mkstemp('.svg') with open(svg_file, 'w') as svg_handle: svg_handle.write(svg) # transform to hpgl hpgl = svgToHPGL(svg_path) # remove tmp file os.remove(svg_path) r = Response(hpgl, mimetype='application/hpgl') r.headers.extend({ 'Content-Disposition': 'attachment; filename="cobbled-paths.hpgl"' }) return r if __name__ == '__main__': app.run(debug=True, host='0.0.0.0')