You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

396 lines
11 KiB

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
# ------------------------------
collection = {
'stroke': { 'ascii': ' | ' , 'fonts': [] },
'script': { 'ascii': ' _/' , 'fonts': [] },
'block': { 'ascii': '|_|' , 'fonts': [] },
'outline': { 'ascii': '/ /' , 'fonts': [] },
'effect': { 'ascii': ': :' , 'fonts': [] },
'pattern': { 'ascii': '(((' , 'fonts': [] },
'fill': { 'ascii': '###' , 'fonts': [] },
'3d': { 'ascii': '_|/' , 'fonts': [] },
# 'directions': { '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("[\$]", "<span class='fix'>", ascii)
ascii = re.sub("[\€]", "</span>", 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
def parse_collection():
# 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 databases out of first folder
database = root.split('/')[-2]
# get font type out of last folder
type = root.split('/')[-1]
# only include selected types
if type in collection:
f = {}
f['name'] = name
f['database'] = database
f['figfont'] = figfont
# sort them by type
collection[type]['fonts'].append(f)
# 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 'index',
'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']
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 'font_index',
'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/<id>")
def writing(id):
params = {
'text': request.args.get('t') or 'the quick brown fox jumps over the lazy dog',
'pad': id or 'ascriipt',
'weight': request.args.get('w') or '3',
}
if '.flf' in params['pad']:
# it's not a pad it's a local figfont file
figfont = '/'.join(params['pad'].split('_'))
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:
# we compile a figfont from a pad
params['pad-full'] = etherpad + prefix + params['pad']
pad_answer = get_pad(params['pad-full'])
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 'the quick brown fox jumps over the lazy dog',
'weight': request.args.get('w') or '3',
}
return render_template(
'catalogue.html',
title = title,
databases = databases,
collection = collection,
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'<svg xmlns="http://www.w3.org/2000/svg" viewbox="{viewbox}" width="{newWidth}mm" height="{newHeight}mm" class="svgbob">'
@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 = pad_export.text
# to SVG
svg = ascii2svg(ascii, params['weight'])
# Remove background rect inserted by SVG Bob
svg = re.sub(r'\<rect class="backdrop" x="\d+" y="\d+" width="\d+" height="\d+">\<\/rect\>', '', svg, flags=re.M)
svg = re.sub(r'<svg xmlns="http://www.w3.org/2000/svg" width="(\d+)" height="(\d+)" class="svgbob">', 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__':
parse_collection()
app.run(debug=True, host='0.0.0.0')