Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!doctype html>
- <html><head>
- <meta charset="utf-8">
- <title>generate img</title>
- <script>
- //
- // generate_img.html version 0.0.99.20211018 (2021-10-18) written by cleemy desu wayo
- //
- // this is a personal work and is a PDS (Public Domain Software)
- //
- // see https://twitter.com/cleemy4545/status/1450188684045787142 and the thread
- //
- // old version of generate_img.html : https://pastebin.com/wEG4s4Eu
- //
- // another edition:
- // generate_img.py (Python + Pillow) : https://pastebin.com/geUDPrWT
- // generate_img.rb (Ruby + RMagick) : https://pastebin.com/2pEVnM8Q
- // generate_img.js (Node.js + node-canvas) : https://pastebin.com/wgfaHUKh
- //
- 'use strict';
- let dirImg = './';
- let dirTextimg = './';
- let isDirTextimgOverridden = false;
- let mainCanvasElem = null; // after loading the page, this always points to either of the two canvas elements
- let mainCanvas = null; // will be set to null every time the redrawing starts
- let mainCanvasContext = null;
- let newImgPiece = new Image;
- let textimgParams = new Map([['body', ''], ['x', 0], ['y', 0], ['charPos', 0]]);
- let pos = new Map([['x', 0], ['y', 0]]);
- let margin = new Map([['x', 0], ['y', 0]]);
- let step = new Map([['x', 1], ['y', 0]]);
- let textSize = 16;
- let textColor = [];
- let textFont = 'unifont';
- let textStep = 1;
- let generateCount = 0;
- let lines = [];
- let lineNum = -1;
- let loopCount = 0;
- let isImgLoading = false;
- const waitMillisec = 0;
- //
- // parseArgs(sourceStr): parse sourceStr and return an Array
- //
- // '(100,100,20,#ffffff)' --> return ['100', '100', '20', '#ffffff']
- // '(100)' --> return ['100']
- // ' (100) ' --> return ['100']
- // '( 100 , 100 )' --> return ['100', '100']
- // '( 100 , 10 0 )' --> return ['100', '10 0']
- // '(,,20)' --> return ['', '', '20']
- // '(20,,)' --> return ['20', '', '']
- // '(,,,)' --> return ['', '', '', '']
- // '( , , , )' --> return ['', '', '', '']
- // '()' --> return ['']
- // '( )' --> return ['']
- // '' --> return []
- // '(())' --> return ['()']
- // '(100,100' (invalid string) --> return []
- // [100, 100] (not string) --> return []
- //
- const parseArgs = function(sourceStr) {
- if (Object.prototype.toString.call(sourceStr) !== '[object String]') {
- return [];
- }
- const args_str = sourceStr.trim().replace(/^\((.*)\)$/, '$1'); // ' (100,100) ' --> '100,100'
- if (args_str === sourceStr) {
- return [];
- }
- return args_str.split(',').map(s => s.trim());
- }
- //
- // properFilePath(filePath, baseFilePath): return a proper file path
- //
- // this ignores base_file_path_src if file_path starts with '/', 'file:///',
- // 'https://', 'ftp://' or some such
- //
- const properFilePath = function(filePath, baseFilePathSrc = '') {
- // has a control code?
- if (`${filePath}${baseFilePathSrc}`.match(/[\x00-\x1f\x7f-\x9f]/)) { return '' }
- let resultStr = '';
- if (filePath.startsWith('/') || filePath.startsWith('file:///')) {
- resultStr = filePath.replace(/^(file:)?\/+/, 'file:///');
- } else if (filePath.match(/^[a-zA-Z]+:\/\//)) {
- resultStr = filePath.replace(/^([a-zA-Z]+):\/\/+/, '$1://');
- } else {
- let baseFilePath = baseFilePathSrc;
- if (baseFilePath === null || baseFilePath === undefined || baseFilePath === '') {
- baseFilePath = '.';
- } else if (baseFilePath.startsWith('/') || baseFilePath.startsWith('file:///')) {
- baseFilePath = baseFilePath.replace(/^(file:)?\/+/, 'file:///');
- } else if (baseFilePath.match(/^[a-zA-Z]+:\/\//)) {
- baseFilePath = baseFilePath.replace(/^([a-zA-Z]+):\/\/+/, '$1://');
- } else {
- baseFilePath = './' + baseFilePath.replace(/^(\.\/+)+/, '');
- }
- resultStr = baseFilePath.replace(/\/+$/, '') + '/' + filePath.replace(/^(\.\/+)+/, '');
- }
- if (!configFilePrefixAllowlist.some(str => resultStr.startsWith(str))) {
- resultStr = '';
- }
- return resultStr;
- }
- //
- // pasteImg(params): draw new image
- //
- const pasteImg = function(params) {
- mainCanvasContext.drawImage(newImgPiece, params.get('x'), params.get('y'));
- return [newImgPiece.width, newImgPiece.height];
- }
- //
- // drawTextimg(): draw textimg
- //
- const drawTextimg = function() {
- const d = document;
- const c = Array.from(textimgParams.get('body'))[textimgParams.get('charPos')]
- // reached the end
- if (c === undefined) {
- isImgLoading = false;
- return false;
- }
- const filePath = properFilePath('textimg_' + c.codePointAt(0).toString(16) + '.png', dirTextimg);
- d.getElementById('debug_text').value += ' **** load start: ' + filePath + "\n";
- newImgPiece.onload = () => {
- let newImgPieceSize = { x: 0, y: 0 };
- [newImgPieceSize['x'], newImgPieceSize['y']] = pasteImg(textimgParams);
- textimgParams.set('charPos', textimgParams.get('charPos') + 1);
- textimgParams.set('x', textimgParams.get('x') + newImgPieceSize['x']); // "textimg:" does not change pos
- setTimeout(() => {drawTextimg()}, d.getElementById('wait_millisec').value);
- }
- newImgPiece.onerror = () => {
- textimgParams.set('charPos', textimgParams.get('charPos') + 1);
- setTimeout(() => {drawTextimg()}, d.getElementById('wait_millisec').value);
- }
- newImgPiece.src = filePath;
- }
- const drawStart = function() {
- const d = document;
- const generateCountBuffer = generateCount + 1;
- d.getElementById('debug_text').value = "draw start... (" + (new Date().toISOString()) + ")\n";
- if (mainCanvasContext) { mainCanvasContext.clearRect(0, 0, mainCanvas.width, mainCanvas.height); }
- dirImg = './';
- dirTextimg = './';
- mainCanvas = null;
- mainCanvasContext = null;
- pos = new Map([['x', 0], ['y', 0]]);
- margin = new Map([['x', 0], ['y', 0]]);
- step = new Map([['x', 1], ['y', 0]]);
- textSize = 16;
- textColor = [];
- textFont = 'unifont';
- textStep = 1;
- generateCount = generateCountBuffer;
- lines = d.getElementById('main_text').value.split('\n');
- lineNum = -1;
- loopCount = 0;
- isImgLoading = false;
- setTimeout(() => {parseAndExec(generateCountBuffer)}, d.getElementById('wait_millisec').value);
- }
- const parseAndExec = function(count) {
- const d = document;
- const generateCountBuffer = generateCount;
- const isImgLoadingBuffer = isImgLoading;
- const imgLodingStatusStr = ' ... isImgLoading = ' + ((isImgLoadingBuffer) ? 'true (waiting...)' : 'false');
- if (count < generateCountBuffer) {
- return false; // newer redrawing has already started
- }
- loopCount += 1;
- if (! isImgLoadingBuffer) {
- lineNum += + 1;
- }
- const line = lines[lineNum];
- // reached the end
- if (line === null || line === undefined) {
- const exitMessage = (mainCanvas === null) ? "error: no background image\n" : "done.\n";
- d.getElementById('debug_text').value += exitMessage;
- return false;
- }
- d.getElementById('debug_text').value += '(loop count: ' + loopCount + ') ' +
- ' at line [' + (lineNum + 1) + '] ' +
- imgLodingStatusStr + "\n";
- // now waiting
- if (isImgLoadingBuffer) {
- setTimeout(() => {parseAndExec(generateCountBuffer)}, d.getElementById('wait_millisec').value);
- return false;
- }
- let m = null;
- if (m = line.match(/^dir *: *([-_a-zA-Z0-9./%:]+) *$/)) {
- dirImg = properFilePath(m[1]);
- if (!isDirTextimgOverridden) { dirTextimg = dirImg }
- } else if (m = line.match(/^dir *\( *textimg *\) *: *([-_a-zA-Z0-9./%:]+) *$/)) {
- dirTextimg = properFilePath(m[1]);
- isDirTextimgOverridden = true;
- } else if (m = line.match(/^bg *: *([-_a-zA-Z0-9./%:]+) *$/)) {
- const bgSrc = properFilePath(m[1], dirImg);
- d.getElementById('debug_text').value += ' **** load bg start: ' + bgSrc + "\n";
- newImgPiece.src = bgSrc;
- isImgLoading = true;
- newImgPiece.onload = () => {
- mainCanvas = mainCanvasElem;
- mainCanvas.setAttribute('width', newImgPiece.width);
- mainCanvas.setAttribute('height', newImgPiece.height);
- mainCanvasContext = mainCanvas.getContext('2d');
- mainCanvasContext.drawImage(newImgPiece, 0, 0);
- isImgLoading = false;
- }
- newImgPiece.onerror = () => {
- isImgLoading = false;
- }
- } else if ((m = line.match(/^paste *(\(.*?\))? *: *([-_a-zA-Z0-9./%:]+) *$/)) && mainCanvas) {
- const filePath = properFilePath(m[2], dirImg);
- let pasteParams = new Map([['src', filePath ],
- ['x', pos.get('x')],
- ['y', pos.get('y')]]);
- const pasteArgs = parseArgs(m[1]);
- const argsLength = pasteArgs.length;
- if (argsLength >= 1) {
- if (pasteArgs[0].match(/^-?[0-9]+(\.[0-9]+)?$/)) {
- pasteParams.set('x', pos.get('x') + parseFloat(pasteArgs[0]));
- }
- if ((argsLength >= 2) && pasteArgs[1].match(/^-?[0-9]+(\.[0-9]+)?$/)) {
- pasteParams.set('y', pos.get('y') + parseFloat(pasteArgs[1]));
- }
- }
- d.getElementById('debug_text').value += ' **** load start: ' + pasteParams.get('src') + "\n";
- isImgLoading = true;
- newImgPiece.onload = () => {
- let newImgPieceSize = { x: 0, y: 0 };
- [newImgPieceSize['x'], newImgPieceSize['y']] = pasteImg(pasteParams);
- ['x', 'y'].forEach(axis => {
- pos.set(axis, pos.get(axis) + newImgPieceSize[axis] * step.get(axis) + margin.get(axis));
- });
- isImgLoading = false;
- }
- newImgPiece.onerror = () => {
- isImgLoading = false;
- }
- newImgPiece.src = pasteParams.get('src');
- } else if ((m = line.match(/^textimg *(\(.*?\))? *: *(.+)$/)) && mainCanvas) {
- textimgParams.set('body', m[2]);
- textimgParams.set('x', pos.get('x')); // "textimg:" does not change pos
- textimgParams.set('y', pos.get('y')); // "textimg:" does not change pos
- textimgParams.set('charPos', 0);
- const textimgArgs = parseArgs(m[1]);
- const argsLength = textimgArgs.length;
- if (argsLength >= 1) {
- if (textimgArgs[0].match(/^-?[0-9]+(\.[0-9]+)?$/)) {
- textimgParams.set('x', pos.get('x') + parseFloat(textimgArgs[0]));
- }
- if ((argsLength >= 2) && textimgArgs[1].match(/^-?[0-9]+(\.[0-9]+)?$/)) {
- textimgParams.set('y', pos.get('y') + parseFloat(textimgArgs[1]));
- }
- }
- isImgLoading = true;
- drawTextimg();
- } else if ((m = line.match(/^text *(\(.*?\))? *: *(.+)$/)) && mainCanvas) {
- let textParams = new Map([['body', m[2] ],
- ['x', pos.get('x') ],
- ['y', pos.get('y') ],
- ['size', parseFloat(textSize)],
- ['color', textColor ]]);
- const textArgs = parseArgs(m[1]);
- const argsLength = textArgs.length;
- if (argsLength >= 1) {
- if (textArgs[0].match(/^-?[0-9]+(\.[0-9]+)?$/)) {
- textParams.set('x', pos.get('x') + parseFloat(textArgs[0]));
- }
- if ((argsLength >= 2) && textArgs[1].match(/^-?[0-9]+(\.[0-9]+)?$/)) {
- textParams.set('y', pos.get('y') + parseFloat(textArgs[1]));
- }
- if ((argsLength >= 3) && textArgs[2].match(/^[0-9]+(\.[0-9]+)?$/)) {
- textParams.set('size', parseFloat(textArgs[2]));
- }
- if ((argsLength >= 4) && textArgs[3].startsWith('#')) {
- const matched_rgb = textArgs[3].match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/)
- if (matched_rgb !== null) {
- const matched_rgb_array = matched_rgb.slice(1, 4).map(s => parseInt(s, 16));
- textParams.set('color', matched_rgb_array);
- }
- }
- }
- mainCanvasContext.font = textParams.get('size').toString() + 'px ' + textFont;
- mainCanvasContext.fillStyle = 'rgb(' + textParams.get('color').join(',') + ')';
- mainCanvasContext.fillText(textParams.get('body'),
- textParams.get('x'),
- textParams.get('y') + textParams.get('size'));
- pos.set('y', pos.get('y') + parseFloat(textSize) * parseFloat(textStep) + margin.get('y'));
- } else if (m = line.match(/^text_([a-z]+) *: *(.+?) *$/)) {
- if (m[1] === 'font') {
- textFont = m[2];
- } else if (m[1] === 'size') {
- textSize = parseFloat(m[2]);
- } else if (m[1] === 'color') {
- let regex;
- let radix;
- if (m[2].startsWith('#')) {
- regex = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2}) *$/;
- radix = 16;
- } else {
- regex = /^([0-9]+) *, *([0-9]+) *, *([0-9]+) *$/;
- radix = 10;
- }
- const matched_rgb = m[2].match(regex);
- if (matched_rgb !== null) {
- textColor = matched_rgb.slice(1, 4).map(s => parseInt(s, radix));
- }
- } else if (m[1] === 'step') {
- textStep = parseFloat(m[2]);
- }
- } else if (m = line.match(/^(blank|margin|pos|step) *(\(.*?\))? *: *([-0-9.]+?) *$/)) {
- const args = parseArgs(m[2]);
- if ((args.length !== 0) && (['x', 'y', ''].indexOf(args[0]) < 0)) {
- return false;
- }
- let axes = ['x', 'y'];
- if (['x', 'y'].indexOf(args[0]) >= 0) {
- axes = [args[0]];
- }
- axes.forEach(axis => {
- switch (m[1]) {
- case 'blank' : pos.set (axis, pos.get(axis) + parseFloat(m[3])); break;
- case 'margin' : margin.set (axis, parseFloat(m[3])); break;
- case 'pos' : pos.set (axis, parseFloat(m[3])); break;
- case 'step' : step.set (axis, parseFloat(m[3])); break;
- }
- });
- }
- setTimeout(() => {parseAndExec(generateCountBuffer)}, d.getElementById('wait_millisec').value);
- }
- let watchCount = 0;
- let keepAfterChangeCount = 0;
- let lastMainText = '';
- const textareaWatch = function() {
- const d = document;
- watchCount += 1;
- if (keepAfterChangeCount >= 1) {
- keepAfterChangeCount += 1;
- }
- d.getElementById('change_count_label').textContent = watchCount +
- ' (' + keepAfterChangeCount + ')' +
- ' (' + generateCount + ')';
- if (lastMainText !== d.getElementById('main_text').value) {
- keepAfterChangeCount = 1;
- } else if (keepAfterChangeCount > 9) {
- keepAfterChangeCount = 0;
- drawStart();
- }
- lastMainText = d.getElementById('main_text').value;
- setTimeout(() => {textareaWatch()}, 100);
- }
- window.addEventListener('load', () => {
- mainCanvasElem = document.getElementById('main_canvas');
- document.getElementById('debug_text').value = '';
- lastMainText = document.getElementById('main_text').value;
- textareaWatch();
- configChange();
- drawStart();
- toolsTextimgFilenameGenerate();
- });
- let subWindow;
- const createNewWindow = function() {
- document.getElementById('btn_new_window').disabled = true;
- document.getElementById('main_canvas').style.display = 'none';
- const newWindowHtml =
- '<html><head><title>generate_img.html canvas</title>' +
- '<style>' +
- '* { margin: 0 0 0 0; }' +
- 'button { display: inline-block; font-size: 0.9rem; white-space: normal; ' +
- ' padding: 0 0 0 0; max-width: 16rem; min-width: 3rem; width: 50%; min-height: 2.4rem; }' +
- '</style>' +
- '<script>' +
- 'window.onkeydown = (e) => { if (e.keyCode === 27) { window.close(); }};' +
- 'const toFullScreen = () => { document.getElementById(\'main_canvas_on_sub_window\').requestFullscreen(); };' +
- '</' + 'script' + '>' +
- '</head><body>' +
- '<header style="position: fixed; max-width: 45rem; width: 100%;">' +
- '<button onclick="toFullScreen();">full screen</button>' +
- '<button onclick="window.close();" style="float: right;">close this window</button>' +
- '</header>' +
- '<main id="main_application_on_sub_window">' +
- '<canvas id="main_canvas_on_sub_window" style="margin-top: 2.4rem;"' +
- ' onclick="document.exitFullscreen();"' +
- ' ondblclick="toFullScreen();"' +
- '></canvas>' +
- '</main>' +
- '</body></html>';
- subWindow = window.open('', '_blank', 'resizable');
- subWindow.document.open();
- subWindow.document.write(newWindowHtml);
- subWindow.document.close();
- watchSubWindowAndRedraw();
- }
- const watchSubWindowAndRedraw = function() {
- const subCanvas = subWindow.document.getElementById('main_canvas_on_sub_window');
- const d = document;
- if (subCanvas !== null) {
- mainCanvasElem = subCanvas;
- drawStart();
- subWindow.addEventListener('unload', () => {
- mainCanvasElem = d.getElementById('main_canvas');
- d.getElementById('main_canvas').style.display = 'block';
- drawStart();
- d.getElementById('btn_new_window').disabled = false;
- });
- } else {
- setTimeout(() => { watchSubWindowAndRedraw() }, 200);
- }
- }
- let configFilePrefixAllowlist = ['./', 'file:///'];
- const configChange = function() {
- const d = document;
- configFilePrefixAllowlist = ['./', 'file:///'];
- if (d.getElementById('config_allow_external_http').checked) { configFilePrefixAllowlist.push('http://'); }
- if (d.getElementById('config_allow_external_https').checked) { configFilePrefixAllowlist.push('https://'); }
- drawStart();
- }
- let lastToolsTextimgFilename = '';
- const toolsTextimgFilenameGenerate = function() {
- const d = document;
- const tmpStr = d.getElementById('tools_textimg_filename_text').value;
- if (lastToolsTextimgFilename !== tmpStr) {
- lastToolsTextimgFilename = tmpStr;
- let result_html = '';
- let result_text = '';
- Array.from(tmpStr).forEach(c => {
- const codePointStr = c.codePointAt(0);
- const codePointStrHex = codePointStr.toString(16);
- result_html += '"&#x' + codePointStrHex + ';" (' + codePointStr + ', 0x' + codePointStrHex + ')';
- result_html += ' ---- textimg_' + codePointStrHex + '.png';
- result_html += '<br>';
- result_text += '"' + c + '" (' + codePointStr + ', 0x' + codePointStrHex + ')';
- result_text += ' ---- textimg_' + codePointStrHex + '.png';
- result_text += "\n";
- });
- //d.getElementById('tools_textimg_filename_result').innerHTML = result_html;
- d.getElementById('tools_textimg_filename_result').innerText = result_text;
- let styleStr = 'none';
- if (result_html !== '') { styleStr = 'block'; }
- d.getElementById('tools_textimg_filename_result').style.display = styleStr;
- }
- setTimeout(() => { toolsTextimgFilenameGenerate() }, 800);
- }
- </script>
- <style>
- * { margin: 0 0 0 0; }
- main { display: block; white-space: nowrap; }
- textarea, input { background-color: #001319; color: #aabbcc; }
- body, p, aside { background-color: #112636; }
- body { padding: 0 1rem 0 1rem; }
- p, aside { color: #aabbcc; }
- button { height: 2.2rem; font-size: 1.2rem; }
- aside.footer_aside {
- background-color: #33384a;
- margin: 2rem 1rem 1.5rem 1rem;
- padding: 0 1.2rem 1.2rem 1.2rem;
- border-radius: 0.8rem;
- }
- aside.footer_aside p:first-child {
- padding: 0.6rem 0 0.6rem 0;
- }
- aside.footer_aside p {
- background-color: #33384a;
- }
- aside.footer_aside p.tools_result {
- display: none;
- background-color: #001319;
- background-color: #232738;
- margin: 0.8rem 0.8rem 0.8rem 0.8rem;
- padding: 0.8rem 0.8rem 0.8rem 0.8rem;
- border-radius: 0.8rem;
- width: 30rem;
- font-size: 0.9rem;
- }
- aside.footer_aside p label {
- margin: 0 0.6rem 0 0.2rem;
- }
- aside#config p.config {
- padding: 0.2rem 1rem 0.6rem 0.6rem;
- }
- canvas#main_canvas { display: inline; vertical-align: top; margin-top: 2px;}
- button#btn_new_window { display: inline; font-size: 0.9rem; }
- textarea#main_text { display: inline; vertical-align: top; font-size: 1.1rem; }
- textarea#debug_text { width: 90%; font-size: 0.75rem; }
- aside#debug_info p label { margin: 0 0.6rem 0 2rem; }
- aside#debug_info input { font-size:0.9rem; padding:0 0 0 0; }
- </style>
- </head>
- <body>
- <aside id="version_info"><p>Hi! I'm cleemy desu wayo. This is generate_img.html version 0.0.99.20211018 (2021-10-18)</p></aside>
- <main id="main_application">
- <textarea id="main_text" rows="20" cols="40"></textarea>
- <div style="display: inline-block; vertical-align: top;">
- <p><button id="btn_new_window" onclick="createNewWindow();">open new window</button></p>
- <canvas id="main_canvas" ondblclick="createNewWindow();"></canvas>
- </div>
- <div style="display:none;"><button id="btn_exec" onclick="drawStart();">Exec</button></div>
- </main>
- <aside id="config" class="footer_aside">
- <p>config:</p>
- <p class="config">
- <label>allow external resources:</label>
- <input type="checkbox" size="40" id="config_allow_external_https" onchange="configChange();"><label>https</label>
- <input type="checkbox" size="40" id="config_allow_external_http" onchange="configChange();"><label>http</label>
- </p>
- <p class="config">
- <label>main loop wait:</label>
- <input type="text" size="4" id="wait_millisec" value="0">
- </p>
- </aside>
- <aside id="debug_info" class="footer_aside">
- <p>debug info:</p>
- <textarea readonly rows="5" id="debug_text"></textarea>
- <p>
- <span id="change_count_label">--</span>
- </p>
- </aside>
- <aside id="tools" class="footer_aside">
- <p>tools:</p>
- <p>
- <label>textimg filename check:</label><input type="text" size="40" id="tools_textimg_filename_text">
- </p>
- <p id="tools_textimg_filename_result" class="tools_result"></p>
- </aside>
- </body></html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement