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(text, figfont): print('--- FIGLET SUBPROCESS') figlet = subprocess.run(["figlet", text, "-f", figfont, "-w", "160"], stdout = subprocess.PIPE, stderr = subprocess.PIPE, text=True) print(figlet.stdout) if figlet.returncode == 0: answer = (True, figlet.stdout) else: answer = (False, figlet.stderr) return answer def ascii2svg(ascii, weight): if ascii: print('--- SVGBOB SUBPROCESS') svgbob = subprocess.run(["svgbob_cli", '--stroke-width', weight], input = ascii, stdout = subprocess.PIPE, text=True) return svgbob.stdout else: return "ERROR: etherpad request failed" def ascii_autofix(ascii): print('--- REGEX AUTOFIX') 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 def get_pad(url): # get pad content print('--- ETHERPAD REQUEST') print(url) pad_export = requests.get(url + '/export/txt') if pad_export.status_code == requests.codes.ok: answer = (True, pad_export.text) else: answer = (False, "ERROR: etherpad request failed") return answer def make_figfont(ascii): print('--- MAKE TEMP FIGFONT') (figfont_file, figfont_path) = tempfile.mkstemp(suffix='.flf') print(figfont_path) with open(figfont_path, 'w') as figfont_file: figfont_file.write(ascii) return figfont_path # 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'] pad_answer = get_pad(params['pad-full']) if pad_answer[0]: ascii = pad_answer[1] svg = ascii2svg(ascii, params['weight']) else: svg = pad_answer[1] 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 'the quick brown fox jumps over the lazy dog', '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 'the quick brown fox jumps over the lazy dog', 'pad': id or 'standard', 'weight': request.args.get('w') or '3', } params['pad-full'] = etherpad + prefix + params['pad'] pad_answer = get_pad(params['pad-full']) # TODO: only create new file if content of pad changed # store as a temporary file if pad_answer[0]: ascii = pad_answer[1] figfont = make_figfont(ascii) figlet_answer = text2figlet(params['text'], figfont) if figlet_answer[0]: ascii = figlet_answer[1] svg = ascii2svg(figlet_answer[1], params['weight']) else: ascii = svg = figlet_answer[1] else: ascii = svg = pad_answer[1] return render_template( 'writing.html', title = title, params = params, ascii = ascii, 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 def resizeSVG (m): width = int(m.group(1)) height = int(m.group(2)) viewbox = f'0 0 {width} {height}' newHeight = 420 newWidth = (width/height) * newHeight return f'' @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 = pad_export.text # to SVG svg = ascii2svg(ascii, params['weight']) # Remove background rect inserted by SVG Bob svg = re.sub(r'\\<\/rect\>', '', svg, flags=re.M) svg = re.sub(r'', resizeSVG,svg) # store as a temporary file (svg_file, svg_path) = tempfile.mkstemp('.svg') (hpgl_file, hpgl_path) = tempfile.mkstemp('.hpgl') with open(svg_file, 'w') as svg_handle: svg_handle.write(svg) output = subprocess.run([ "vpype", "read", "--single-layer", svg_path, "scaleto", "297mm", "420mm", "linemerge", "-t", "0.25mm", "linesort", "write", "--device", "dxy", "--color-mode", "none", "--page-size", "a3", "--landscape", hpgl_path ]) with open(hpgl_file, 'r') as hpgl_handle: r = Response(hpgl_handle.read(), mimetype='application/hpgl') r.headers.extend({ 'Content-Disposition': f'attachment; filename="cobbled-paths-{id}.hpgl"' }) # remove tmp file os.remove(svg_path) os.remove(hpgl_path) return r if __name__ == '__main__': app.run(debug=True, host='0.0.0.0')