now with jp2a

This commit is contained in:
Doriane 2024-10-04 18:08:58 +02:00
parent 66c1dcf682
commit a16e0457d4
18 changed files with 377 additions and 319 deletions

View File

@ -61,11 +61,10 @@ Please follow the [installation instructions](https://github.com/abey79/vpype#in
* reload bug in draw
* image tab
* better
* group all js in js folder
* default dic in one place
* input listen when opening page (for browser history remember)
* factorise JS
* factorise CSS
* show font-info file
-->

95
app.py
View File

@ -82,6 +82,25 @@ def text2figlet(text, figfont):
return answer
def image2ascii(img, chars, width):
print('--- JP2A SUBPROCESS')
jp2a = subprocess.run([
"jp2a", img, "--background=light", "--width="+width, "--chars="+chars],
stdout = subprocess.PIPE, stderr = subprocess.PIPE, text=True)
# "--background", "light",
# "--width", width,
# "--chars", chars
print(jp2a)
if jp2a.returncode == 0:
answer = (True, jp2a.stdout)
else:
answer = (False, jp2a.stderr)
return answer
def ascii2svg(ascii, weight='2', scale='1'):
if ascii:
print('--- SVGBOB SUBPROCESS')
@ -272,11 +291,12 @@ def drawing(id):
svg = pad_answer[1]
return render_template(
'drawing.html',
'iframe/drawing.html',
title = title,
params = params,
svg = svg)
# __ _ _ _
# / _| ___ _ __ | |_ _ __ ___ __ _| | _(_)_ __ __ _
# | |_ / _ \| '_ \| __| | '_ ` _ \ / _` | |/ / | '_ \ / _` |
@ -340,7 +360,7 @@ def writing(id):
ascii = svg = pad_answer[1]
return render_template(
'writing.html',
'iframe/double.html',
title = title,
params = params,
ascii = ascii,
@ -395,6 +415,77 @@ def specimen(type):
params = params,
svg = svg)
# _
# (_)_ __ ___ __ _ __ _ ___
# | | '_ ` _ \ / _` |/ _` |/ _ \
# | | | | | | | (_| | (_| | __/
# |_|_| |_| |_|\__,_|\__, |\___|
# |___/
#
# jp2a to svgbob
upload_folder = os.path.join('static', 'upload')
app.config['UPLOAD'] = upload_folder
@app.route("/image.html", methods=['GET', 'POST'])
def image():
# ()/\|'-._=+
params = {
'weight': request.args.get('w') or '2',
'chars': request.args.get('c') or " _|+#",
'width': request.args.get('width') or "120"
}
if request.method == 'POST':
file = request.files['img']
filename = file.filename
file.save(os.path.join(app.config['UPLOAD'], filename))
img = os.path.join(app.config['UPLOAD'], filename)
return render_template('image.html',
title = title,
img = img,
filename = filename,
params = params)
return render_template(
'image.html',
title = title,
img = os.path.join(app.config['UPLOAD'], 'default.jpg'),
filename = 'default.jpg',
params = params)
@app.route("/drawing-image/<id>")
def drawing_image(id):
params = {
'filename': id or 'default.jpg',
'weight': request.args.get('w') or '2',
'chars': request.args.get('chars') or " _|+#",
'width': request.args.get('width') or "120",
}
if params['filename'] == 'default.jpg':
ascii = svg = ''
elif '.' in params['filename']:
img = os.path.join(app.config['UPLOAD'], params['filename'])
answer, ascii = image2ascii(img, params['chars'], params['width'])
if answer:
svg = ascii2svg(ascii, params['weight'])
else:
ascii = svg = answer
return render_template(
'iframe/double.html',
title = title,
params = params,
ascii = ascii,
svg = svg)
# _ _ _
# | |__ _ __ __ _| | _____ ___ __ ___ _ __| |_
# | '_ \| '_ \ / _` | | / _ \ \/ / '_ \ / _ \| '__| __|

View File

@ -67,7 +67,7 @@
{% for catalogue in databases.keys() %}
{% set fonts = type_data['fonts']|selectattr('catalogue', 'equalto', catalogue) %}
{% for f in fonts %}
<div class="font {{f['catalogue']}}">
<div class="split-screen {{f['catalogue']}}">
<h2>{{f['name']}} ({{f['catalogue']}})</h2>

View File

@ -1,4 +1,11 @@
:root{
--bar-h: 3rem;
--c-link: blue;
--c-back: whitesmoke;
--c-default: black;
--c-contributed: palegreen;
--c-jave: mediumpurple;
}
body{
font-family: monospace;
@ -6,6 +13,15 @@ body{
line-height: 1.45;
}
button, input[type="submit"], input[type="file"]{
background-color: var(--c-link);
border: none;
border-radius: 5em;
padding: 0.5em 1em;
color: white;
}
a{
color: var(--c-link);
}
@ -49,22 +65,27 @@ label, input{
position: fixed;
top: 0.5rem;
right: 0.5em;
width: fit-content;
margin-left: auto;
}
#save-buttons > input,
#save-buttons > button,
#save-buttons > label{
margin-bottom: 0.5rem;
margin-left: auto;
}
#save-buttons.direct{
top: calc(var(--bar-h) * 2 + 0.5rem);
right: 1.5em;
}
.double-font{
.double{
height: 100vh;
display: grid;
grid-template-rows: 1fr 1fr;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin: 0;
}
.double-font > div{
.double > div{
background-color: white;
border: 1px solid black;
overflow: auto;
@ -72,9 +93,20 @@ label, input{
font-family: monospace;
font-size: 1rem;
}
.double .f-ascii{
font-size: 0.5rem;
}
.double svg{
transform-origin: top left;
transform: scale(0.5);
}
.f-ascii{
font-family: monospace;
font-size: 1rem;
font-size: .875rem;
background-color: white;
overflow: auto;
line-height: 1.2;
}

View File

@ -1,12 +1,4 @@
:root{
--bar-h: 3rem;
--c-link: blue;
--c-back: whitesmoke;
--c-default: black;
--c-contributed: palegreen;
--c-jave: mediumpurple;
}
.default{
--c: var(--c-default);
}
@ -20,14 +12,6 @@ body{
background-color: var(--c-back);
}
button{
background-color: var(--c-link);
border: none;
border-radius: 5em;
padding: 0.5em 1em;
color: white;
}
img{
display: block;
max-width: 100%;
@ -39,18 +23,6 @@ img{
body{
margin-top: calc(var(--bar-h) * 1);
}
body.write,
body.catalogue,
body.draw{
margin-top: calc(var(--bar-h) * 2);
}
body.write .font,
body.catalogue .font,
body.draw .font{
height: calc(100vh - var(--bar-h) * 2);
grid-template-rows: 1fr;
box-sizing: border-box;
}
body > .tabs{
position: fixed;
@ -144,7 +116,7 @@ nav ul a.active{
/* one font block */
.font{
.split-screen{
display: grid;
grid-template-columns: repeat(2, calc(50% - 0.5rem));
gap: 1rem;
@ -152,13 +124,47 @@ nav ul a.active{
position: relative;
}
.font aside{
.split-screen aside{
position: absolute;
bottom: 0.5rem;
display: flex;
gap: 0.5rem;
}
body.write .split-screen,
body.catalogue .split-screen,
body.image .split-screen,
body.draw .split-screen{
height: calc(100vh - var(--bar-h) * 2);
grid-template-rows: 1fr;
box-sizing: border-box;
}
body.write,
body.catalogue,
body.image,
body.draw{
margin-top: calc(var(--bar-h) * 2);
}
body.image .f-image,
body.image .f-ascii{
grid-row: auto;
grid-column: 1;
}
body.image .f-svg{
grid-row: -1 / 1;
grid-column: 2;
}
body.image .f-image img{
width: 100%;
height: 100%;
object-fit: contain;
filter: grayscale(1);
}
aside.right{
right: 2.5rem;
}
@ -166,30 +172,25 @@ aside.left{
right: calc(50vw + 1.5rem);
}
.font h2{
.split-screen h2{
font-size: 0.875rem;
grid-column: 1 / -1;
font-weight: normal;
}
.f-ascii{
font-family: monospace;
font-size: 1rem;
background-color: white;
overflow: auto;
line-height: 1;
}
.f-svg{
background-color: white;
overflow: auto;
}
.f-image,
.f-ascii,
.f-svg{
background-color: white;
overflow: auto;
height: 100%;
width: 100%;
box-sizing: border-box;
grid-row: 1 / span 1;
border: 1px solid black;
font-family: monospace;
font-size: 1rem;
line-height: 1;
}
.f-svg iframe{
border: none !important;
@ -198,19 +199,24 @@ aside.left{
display: block;
}
body.image .split-screen{
grid-template-columns: 1fr 2fr;
}
.f-double{
width: 100%;
height: 100%;
}
.font:first-of-type{
.split-screen:first-of-type{
margin-top: 1rem;
}
/* TITLE
================================================= */
.title.font{
.title.split-screen{
padding: 2rem 2rem 4rem;
gap: 0 var(--bar-h);
grid-template-columns: repeat(2, calc(50% - calc(var(--bar-h) / 2)));
@ -235,7 +241,7 @@ aside.left{
overflow: visible;
max-width: 100%;
grid-column: span 2;
max-height: 24rem;
max-height: 21rem;
margin: 0 auto;
}
.title svg a{
@ -282,14 +288,14 @@ aside.left{
/* font
================================================= */
.write .font{
.write .split-screen{
grid-template-columns: 32rem 1fr;
}
/* catalogue
================================================= */
.catalogue .font{
.catalogue .split-screen{
grid-template-columns: 1fr;
}

View File

@ -0,0 +1,55 @@
function toggle_class(classname, val){
if(val){
document.body.classList.add(classname);
}
else{
document.body.classList.remove(classname);
}
}
let body_class_checkboxes = document.getElementsByClassName("body-class-check");
for(let checkbox of body_class_checkboxes){
let classname = checkbox.value;
checkbox.addEventListener('input', function(){
toggle_class(classname, checkbox.checked);
});
toggle_class(classname, checkbox.checked);
}
let save_button_svg = document.getElementById('save-svg');
save_button_svg.addEventListener('click', function(){
let url = document.URL,
parts = url.split('/'),
name = parts[parts.length-1],
svg_url = '/svg/' + name,
a = document.createElement('a');
a.href = svg_url;
a.setAttribute('download', 'download');
if (document.createEvent) {
const event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
a.dispatchEvent(event);
}
else {
a.click();
}
});
let save_button_hpgl = document.getElementById('save-hpgl');
save_button_hpgl.addEventListener('click', function () {
let url = document.URL,
parts = url.split('/'),
name = parts[parts.length-1],
hpgl_url = '/hpgl/' + name,
a = document.createElement('a');
a.href = hpgl_url;
a.setAttribute('download', 'download');
if (document.createEvent) {
const event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
a.dispatchEvent(event);
}
else {
a.click();
}
});

View File

@ -0,0 +1,71 @@
let button_pad = document.getElementById('button-pad');
let button_svg = document.getElementById('button-svg');
let svg_iframe = document.getElementById('svg-iframe');
let pad_iframe = document.getElementById('pad-iframe');
let new_url = new URL(svg_iframe.src);
function updateGET(frame, param, value){
// object from GET parameters
let [base_src, params_src] = frame.src.split("?");
let params = new URLSearchParams(params_src);
// update param
params.set(param, value);
// reconstituate URL
let new_src = base_src + "?" + params.toString();
// set and refresh
frame.src = new_src;
}
// --- pad go button
if(button_pad){
button_pad.addEventListener('click', function(){
let input = document.getElementById(button_pad.dataset.use);
let value = input.value;
let pad_src = pad_iframe.src;
pad_src = pad_src.split('-');
pad_src[pad_src.length-1] = value;
pad_src = pad_src.join('-');
pad_iframe.src = pad_src;
let svg_src = svg_iframe.src;
svg_src = svg_src.split('/');
svg_src[svg_src.length-1] = value;
svg_src = svg_src.join('/');
new_url = new URL(svg_src);
svg_iframe.src = new_url;
document.getElementById('main').classList.add("reload");
});
}
// --- svg generation button
if(button_svg){
button_svg.addEventListener('click', function(){
svg_iframe.src = new_url;
document.getElementById('main').classList.add("reload");
});
}
// --- get-input but on the pad and checkbox but on the pad
let inputs = document.getElementsByClassName('get-input');
for(let input of inputs){
input.addEventListener('change', function(){
let frame = document.getElementById(input.dataset.frame);
const url = new URL(frame.src);
if(input.type == 'checkbox'){
url.searchParams.set(input.dataset.name, input.checked);
}
else{
url.searchParams.set(input.dataset.name, input.value);
}
new_url = url;
});
}
svg_iframe.addEventListener("load", function() {
document.getElementById('main').classList.remove("reload");
});

View File

@ -31,7 +31,7 @@
<li><a {% if request.url_rule.endpoint == "draw" %}class="active"{% endif %} href="/draw.html">ASCII draw</a></li>
<li><a {% if request.url_rule.endpoint == "catalogue" %}class="active"{% endif %} href="/catalogue.html">FIGfont catalogue</a></li>
<li><a {% if request.url_rule.endpoint == "font" %}class="active"{% endif %} href="/font.html">FIGfont make</a></li>
<!-- <li><a {% if request.url_rule.endpoint == "image" %}class="active"{% endif %} href="/image.html">image</a></li> -->
<li><a {% if request.url_rule.endpoint == "image" %}class="active"{% endif %} href="/image.html">image</a></li>
<li><a {% if request.url_rule.endpoint == "gallery" %}class="active"{% endif %} href="/gallery.html">gallery</a></li>
</ul>
</nav>

View File

@ -55,7 +55,7 @@
</header>
<div class="font catalogue reload" id="main">
<div class="split-screen catalogue reload" id="main">
<iframe class="f-double" id="svg-iframe"
src="/writing/{{ 'static/db/default/stroke/script.flf'.split('/') | join('$') }}">
</iframe>

View File

@ -18,7 +18,7 @@
</header>
<div class="font reload" id="main">
<div class="split-screen reload" id="main">
<iframe class="f-ascii" id="pad-iframe" src="{{params['pad-full']}}">
</iframe>
<div class="f-svg">
@ -27,75 +27,7 @@
</div>
</div>
<script>
let button_pad = document.getElementById('button-pad');
let button_svg = document.getElementById('button-svg');
let svg_iframe = document.getElementById('svg-iframe');
let pad_iframe = document.getElementById('pad-iframe');
let new_url = new URL(svg_iframe.src);
function updateGET(frame, param, value){
// object from GET parameters
let [base_src, params_src] = frame.src.split("?");
let params = new URLSearchParams(params_src);
// update param
params.set(param, value);
// reconstituate URL
let new_src = base_src + "?" + params.toString();
// set and refresh
frame.src = new_src;
}
// --- pad go button
button_pad.addEventListener('click', function(){
let input = document.getElementById(button_pad.dataset.use);
let value = input.value;
let pad_src = pad_iframe.src;
pad_src = pad_src.split('-');
pad_src[pad_src.length-1] = value;
pad_src = pad_src.join('-');
pad_iframe.src = pad_src;
let svg_src = svg_iframe.src;
svg_src = svg_src.split('/');
svg_src[svg_src.length-1] = value;
svg_src = svg_src.join('/');
new_url = new URL(svg_src);
svg_iframe.src = new_url;
document.getElementById('main').classList.add("reload");
});
// --- svg generation button
button_svg.addEventListener('click', function(){
svg_iframe.src = new_url;
document.getElementById('main').classList.add("reload");
});
// --- get-input but on the pad and checkbox but on the pad
let inputs = document.getElementsByClassName('get-input');
for(let input of inputs){
input.addEventListener('change', function(){
let frame = document.getElementById(input.dataset.frame);
const url = new URL(frame.src);
if(input.type == 'checkbox'){
url.searchParams.set(input.dataset.name, input.checked);
}
else{
url.searchParams.set(input.dataset.name, input.value);
}
new_url = url;
});
}
svg_iframe.addEventListener("load", function() {
document.getElementById('main').classList.remove("reload");
});
</script>
<script src="/static/js/generate_interface.js"></script>
{% endblock %}

View File

@ -1,84 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/static/js/FileSaver.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/basics.css" />
<link rel="stylesheet" type="text/css" href="/static/css/svg.css" />
</head>
<body>
{{ svg|safe }}
<div id="save-buttons">
<label for="text-checkbox"
title="display the remaining text and markers in the svg output in red">
preview</label>
<input id="text-checkbox" type="checkbox" class="body-class-check" value="check-text" data-name="c" data-frame="svg-iframe"/>
<button id="save-svg">get SVG</button>
<button id="save-hpgl">get HPGL</button>
</div>
<script>
function toggle_class(classname, val){
if(val){
document.body.classList.add(classname);
}
else{
document.body.classList.remove(classname);
}
}
let body_class_checkboxes = document.getElementsByClassName("body-class-check");
for(let checkbox of body_class_checkboxes){
let classname = checkbox.value;
checkbox.addEventListener('input', function(){
toggle_class(classname, checkbox.checked);
});
toggle_class(classname, checkbox.checked);
}
</script>
<script>
let save_button_svg = document.getElementById('save-svg');
save_button_svg.addEventListener('click', function(){
let url = document.URL,
parts = url.split('/'),
name = parts[parts.length-1],
svg_url = '/svg/' + name,
a = document.createElement('a');
a.href = svg_url;
a.setAttribute('download', 'download');
if (document.createEvent) {
const event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
a.dispatchEvent(event);
}
else {
a.click();
}
});
let save_button_hpgl = document.getElementById('save-hpgl');
save_button_hpgl.addEventListener('click', function () {
let url = document.URL,
parts = url.split('/'),
name = parts[parts.length-1],
hpgl_url = '/hpgl/' + name,
a = document.createElement('a');
a.href = hpgl_url;
a.setAttribute('download', 'download');
if (document.createEvent) {
const event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
a.dispatchEvent(event);
}
else {
a.click();
}
});
</script>
</body>
</html>

View File

@ -23,7 +23,7 @@
</header>
<div class="font reload" id="main">
<div class="split-screen reload" id="main">
<iframe class="f-ascii" id="pad-iframe" src="{{params['pad-full']}}">
</iframe>
<iframe class="f-double" id="svg-iframe" src="/writing/{{params['pad']}}">

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/static/js/FileSaver.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/basics.css" />
<link rel="stylesheet" type="text/css" href="/static/css/svg.css" />
</head>
<body class="double">
<div class="f-ascii"><pre>{{ ascii|safe }}</pre></div>
<div class="f-svg">
{{ svg|safe }}
{% include 'partials/export_interface.html' %}
</div>
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/static/js/FileSaver.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/basics.css" />
<link rel="stylesheet" type="text/css" href="/static/css/svg.css" />
</head>
<body>
{{ svg|safe }}
{% include 'partials/export_interface.html' %}
</body>
</html>

View File

@ -5,33 +5,32 @@
{% block body %}
<header class="controls">
<button id="button-image">import image</button>
<form action="{{ url_for('image') }}" enctype="multipart/form-data" method="POST">
<input name="img" type="file"/>
<input type="submit"/>
</form>
<hr>
<button id="button-svg">generate</button>
<input class="get-input" style="width: 8rem;" id="text-input" type="text" value="{{params['chars']}}" data-name="chars" data-frame="svg-iframe"/>
<input class="get-input" id="text-input" type="text" value=" ()/\|'-._=+" data-name="t" data-frame="svg-iframe"/>
<label>width</label>
<input class="get-input" type="range" min="40" max="320" step="20" value="{{params['width']}}" data-name="width" data-frame="svg-iframe"/>
<label>weight</label>
<input class="get-input" type="range" min="1" max="8" value="{{params['weight']}}" data-name="w" data-frame="svg-iframe"/>
<label class="text-label" for="text-checkbox"
title="display the remaining text in the svg output in red">
output text</label>
<input id="text-checkbox" type="checkbox" class="get-input"
class="body-class-check" value="check-text" data-name="t" data-frame="svg-iframe" checked/>
</header>
<div class="font">
<iframe class="f-ascii" id="pad-iframe" src="{{params['pad-full']}}">
<div class="split-screen reload" id="main">
<div class="f-image"><img src="{{img}}"></div>
<iframe id="svg-iframe" class="f-double" src="/drawing-image/{{filename}}">
</iframe>
<div class="f-svg">
<iframe id="svg-iframe" src="/drawing/{{params['pad']}}">
</iframe>
</div>
</div>
<script src="/static/js/generate_interface.js"></script>
{% endblock %}

View File

@ -2,7 +2,7 @@
{% block body %}
<div class="font title box">
<div class="split-screen title box">
<div class="f-ascii">
<pre> .- _-- /. /. /. |\
( /| || || || \\

View File

@ -0,0 +1,9 @@
<div id="save-buttons" {% if zone == "direct" %}class="direct"{% endif %}>
<label for="text-checkbox"
title="display the remaining text and markers in the svg output in red">
preview</label>
<input id="text-checkbox" type="checkbox" class="body-class-check" value="check-text" data-name="c" data-frame="svg-iframe"/>
<button id="save-svg">get SVG</button>
<button id="save-hpgl">get HPGL</button>
</div>
<script src="/static/js/export_interface.js"></script>

View File

@ -1,88 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/static/js/FileSaver.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/basics.css" />
<link rel="stylesheet" type="text/css" href="/static/css/svg.css" />
</head>
<body class="double-font">
<div class="f-svg">
{{ svg|safe }}
<div id="save-buttons">
<label for="text-checkbox"
title="display the remaining text in the svg output in red">
preview</label>
<input id="text-checkbox" type="checkbox" class="body-class-check" value="check-text" data-name="c" data-frame="svg-iframe"/>
<button id="save-svg">get SVG</button>
<button id="save-hpgl">get HPGL</button>
</div>
</div>
<div class="f-ascii"><pre>{{ ascii|safe }}</pre></div>
<script>
function toggle_class(classname, val){
if(val){
document.body.classList.add(classname);
}
else{
document.body.classList.remove(classname);
}
}
let body_class_checkboxes = document.getElementsByClassName("body-class-check");
for(let checkbox of body_class_checkboxes){
let classname = checkbox.value;
checkbox.addEventListener('input', function(){
toggle_class(classname, checkbox.checked);
});
toggle_class(classname, checkbox.checked);
}
</script>
<script>
let save_button_svg = document.getElementById('save-svg');
save_button_svg.addEventListener('click', function(){
let url = document.URL,
parts = url.split('/'),
name = parts[parts.length-1],
svg_url = '/svg/' + name,
a = document.createElement('a');
a.href = svg_url;
a.setAttribute('download', 'download');
if (document.createEvent) {
const event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
a.dispatchEvent(event);
}
else {
a.click();
}
});
let save_button_hpgl = document.getElementById('save-hpgl');
save_button_hpgl.addEventListener('click', function () {
let url = document.URL,
parts = url.split('/'),
name = parts[parts.length-1],
hpgl_url = '/hpgl/' + name,
a = document.createElement('a');
a.href = hpgl_url;
a.setAttribute('download', 'download');
if (document.createEvent) {
const event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
a.dispatchEvent(event);
}
else {
a.click();
}
});
</script>
</body>
</html>