export hpgl load from folder
This commit is contained in:
59
webui/app.py
59
webui/app.py
@@ -29,6 +29,7 @@ app.secret_key = webui_secret_key()
|
||||
app.config['MAX_CONTENT_LENGTH'] = webui_max_upload_mb() * 1024 * 1024
|
||||
UPLOAD_DIR = os.path.join(ROOT, 'webui', 'uploads')
|
||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||
OUTPUT_HPGL_DIR = os.path.join(ROOT, 'output', 'hpgl')
|
||||
FONT_DIRS = [
|
||||
os.path.join(ROOT, 'font'),
|
||||
os.path.join(ROOT, '.git', 'font'),
|
||||
@@ -119,6 +120,45 @@ def api_text_to_hpgl():
|
||||
return jsonify({'ok': True, 'filename': session['upload_filename'], 'preview_url': '/api/svg'})
|
||||
|
||||
|
||||
@app.route('/api/hpgl_files')
|
||||
def list_hpgl_files():
|
||||
"""List HPGL filenames in output/hpgl/."""
|
||||
if not os.path.isdir(OUTPUT_HPGL_DIR):
|
||||
return jsonify({'files': []})
|
||||
files = []
|
||||
for name in sorted(os.listdir(OUTPUT_HPGL_DIR)):
|
||||
if name.startswith('.'):
|
||||
continue
|
||||
path = os.path.join(OUTPUT_HPGL_DIR, name)
|
||||
if not os.path.isfile(path):
|
||||
continue
|
||||
low = name.lower()
|
||||
if low.endswith('.hpgl') or low.endswith('.plt') or '.' not in name:
|
||||
files.append(name)
|
||||
return jsonify({'files': files})
|
||||
|
||||
|
||||
@app.route('/api/load_hpgl', methods=['POST'])
|
||||
def load_hpgl_from_folder():
|
||||
"""Set current program from a file in output/hpgl/."""
|
||||
data = request.get_json() or {}
|
||||
filename = (data.get('filename') or '').strip()
|
||||
if not filename:
|
||||
return jsonify({'error': 'No filename'}), 400
|
||||
if os.path.basename(filename) != filename or '..' in filename:
|
||||
return jsonify({'error': 'Invalid filename'}), 400
|
||||
path = os.path.join(OUTPUT_HPGL_DIR, filename)
|
||||
if not os.path.isfile(path):
|
||||
return jsonify({'error': 'File not found'}), 404
|
||||
try:
|
||||
Program.parsefile(path)
|
||||
except Exception as e:
|
||||
return jsonify({'error': 'Invalid HPGL: {}'.format(str(e))}), 400
|
||||
session['upload_path'] = path
|
||||
session['upload_filename'] = filename
|
||||
return jsonify({'ok': True, 'filename': filename, 'preview_url': '/api/svg'})
|
||||
|
||||
|
||||
@app.route('/api/upload', methods=['POST'])
|
||||
def upload():
|
||||
if 'file' not in request.files:
|
||||
@@ -144,6 +184,25 @@ def upload():
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/download_hpgl')
|
||||
def download_hpgl():
|
||||
"""Return current program as downloadable HPGL file."""
|
||||
p = _get_program()
|
||||
if p is None:
|
||||
return jsonify({'error': 'No file loaded'}), 404
|
||||
filename = (session.get('upload_filename') or 'export').strip()
|
||||
if not filename.lower().endswith('.hpgl') and not filename.lower().endswith('.plt'):
|
||||
filename = filename + '.hpgl'
|
||||
return (
|
||||
str(p),
|
||||
200,
|
||||
{
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Disposition': 'attachment; filename="{}"'.format(filename),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@app.route('/api/dimensions')
|
||||
def dimensions():
|
||||
"""Return current program size in plotter units (e.g. 0.025 mm per unit)."""
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
scaleToBounds: document.getElementById('btnScaleToBounds'),
|
||||
print: document.getElementById('btnPrint'),
|
||||
checkPlotter: document.getElementById('btnCheckPlotter'),
|
||||
generateHpgl: document.getElementById('btnGenerateHpgl')
|
||||
generateHpgl: document.getElementById('btnGenerateHpgl'),
|
||||
exportHpgl: document.getElementById('btnExportHpgl')
|
||||
};
|
||||
const tabFile = document.getElementById('tabFile');
|
||||
const tabText = document.getElementById('tabText');
|
||||
@@ -33,6 +34,7 @@
|
||||
const fontSelect = document.getElementById('fontSelect');
|
||||
const textSize = document.getElementById('textSize');
|
||||
const textMessage = document.getElementById('textMessage');
|
||||
const hpglFolderSelect = document.getElementById('hpglFolderSelect');
|
||||
|
||||
function setMessage(el, text, type) {
|
||||
el.textContent = text || '';
|
||||
@@ -93,7 +95,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
var transformPrintButtons = ['flip', 'rotate90', 'rotate180', 'scaleUp', 'scaleDown', 'centralize', 'scaleToBounds', 'print'];
|
||||
var transformPrintButtons = ['flip', 'rotate90', 'rotate180', 'scaleUp', 'scaleDown', 'centralize', 'scaleToBounds', 'print', 'exportHpgl'];
|
||||
|
||||
function setLoaded(loaded) {
|
||||
if (loaded) {
|
||||
@@ -264,9 +266,52 @@
|
||||
tabText.classList.toggle('active', !isFile);
|
||||
if (panelFile) panelFile.classList.toggle('hidden', !isFile);
|
||||
if (panelText) panelText.classList.toggle('hidden', isFile);
|
||||
if (isFile && hpglFolderSelect && hpglFolderSelect.options.length <= 1) loadHpglFiles();
|
||||
if (!isFile && fontSelect && fontSelect.options.length <= 1) loadFonts();
|
||||
}
|
||||
|
||||
function loadHpglFiles() {
|
||||
if (!hpglFolderSelect) return;
|
||||
fetch('/api/hpgl_files')
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
var opts = hpglFolderSelect.querySelectorAll('option');
|
||||
for (var i = opts.length - 1; i >= 1; i--) opts[i].remove();
|
||||
(d.files || []).forEach(function (f) {
|
||||
var o = document.createElement('option');
|
||||
o.value = f;
|
||||
o.textContent = f;
|
||||
hpglFolderSelect.appendChild(o);
|
||||
});
|
||||
})
|
||||
.catch(function () {});
|
||||
}
|
||||
|
||||
if (hpglFolderSelect) {
|
||||
hpglFolderSelect.addEventListener('change', function () {
|
||||
var filename = (this.value || '').trim();
|
||||
if (!filename) return;
|
||||
setMessage(printMessage, '');
|
||||
fetch('/api/load_hpgl', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ filename: filename })
|
||||
})
|
||||
.then(function (r) {
|
||||
return r.json().then(function (d) {
|
||||
if (!r.ok) throw new Error(d.error || 'Load failed');
|
||||
return d;
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
refreshStatus();
|
||||
})
|
||||
.catch(function (e) {
|
||||
setMessage(printMessage, e.message || 'Load failed', 'error');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadFonts() {
|
||||
if (!fontSelect) return;
|
||||
fetch('/api/fonts')
|
||||
@@ -287,6 +332,12 @@
|
||||
if (tabFile) tabFile.addEventListener('click', function () { showTab('file'); });
|
||||
if (tabText) tabText.addEventListener('click', function () { showTab('text'); });
|
||||
|
||||
if (buttons.exportHpgl) {
|
||||
buttons.exportHpgl.addEventListener('click', function () {
|
||||
window.location.href = '/api/download_hpgl';
|
||||
});
|
||||
}
|
||||
|
||||
if (buttons.generateHpgl && textInput && fontSelect) {
|
||||
buttons.generateHpgl.addEventListener('click', function () {
|
||||
var text = (textInput.value || '').trim();
|
||||
@@ -324,6 +375,7 @@
|
||||
}
|
||||
|
||||
refreshStatus();
|
||||
loadHpglFiles();
|
||||
updatePlotterInfo();
|
||||
setInterval(updatePlotterInfo, 10000);
|
||||
})();
|
||||
|
||||
@@ -155,6 +155,23 @@ body {
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
.btn-export-hpgl {
|
||||
width: 100%;
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-export-hpgl:hover:not(:disabled) {
|
||||
background: var(--border);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
<aside class="sidebar">
|
||||
<div id="panelFile" class="tab-panel">
|
||||
<section class="upload-section">
|
||||
<label class="upload-label" for="hpglFolderSelect">Load from folder</label>
|
||||
<select id="hpglFolderSelect" class="font-select">
|
||||
<option value="">— Select file —</option>
|
||||
</select>
|
||||
<label class="upload-label" for="fileInput">Upload HPGL</label>
|
||||
<input type="file" id="fileInput" accept=".hpgl,.plt" class="file-input">
|
||||
<p class="hint">.hpgl or .plt files only</p>
|
||||
@@ -40,6 +44,7 @@
|
||||
<label class="upload-label" for="textSize">Size (pt)</label>
|
||||
<input type="number" id="textSize" class="text-size" value="72" min="8" max="500" step="1">
|
||||
<button type="button" class="btn btn-generate" id="btnGenerateHpgl">Generate HPGL</button>
|
||||
<button type="button" class="btn btn-export-hpgl" id="btnExportHpgl" disabled>Export HPGL</button>
|
||||
<p id="textMessage" class="message"></p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user