mirror of
https://gitlab.constantvzw.org/osp/tools.cobbled-paths.git
synced 2024-12-22 20:50:31 +01:00
283 lines
7.7 KiB
Python
283 lines
7.7 KiB
Python
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("[\$]", "<span class='fix'>", ascii)
|
||
ascii = re.sub("[\€]", "</span>", 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/<id>")
|
||
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)
|
||
|
||
@app.route("/font.html")
|
||
def font():
|
||
|
||
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(
|
||
'font.html',
|
||
title = title,
|
||
params = params)
|
||
|
||
# _ _
|
||
# ___ __ _| |_ __ _| | ___ __ _ _ _ ___
|
||
# / __/ _` | __/ _` | |/ _ \ / _` | | | |/ _ \
|
||
# | (_| (_| | || (_| | | (_) | (_| | |_| | __/
|
||
# \___\__,_|\__\__,_|_|\___/ \__, |\__,_|\___|
|
||
# |___/
|
||
#
|
||
# 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/<id>')
|
||
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'])
|
||
|
||
# store as a temporary file
|
||
(svg_file, svg_path) = tempfile.mkstemp()
|
||
print(svg_path)
|
||
with open(svg_path, 'w') as svg_file:
|
||
svg_file.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') |