bitmapit
Version:
A simple bitmap font generator for creating text art in uppercase letters, designed for ASCII and pixel-style text rendering.
1,110 lines (1,079 loc) • 28.3 kB
JavaScript
var bitmapit = (function (exports) {
'use strict';
const defaultFont = {
'A': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'B': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'C': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'D': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'E': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'F': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'G': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'H': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'I': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'J': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'K': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'L': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'M': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'N': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'O': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'P': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'Q': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'R': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'S': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'T': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'U': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'V': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'W': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'X': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'Y': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'Z': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'0': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'1': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'2': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'3': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'4': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'5': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'6': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'7': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'8': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'9': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
' ': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'.': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'!': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'@': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'#': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'$': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'%': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'&': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'*': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'+': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'-': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'=': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'?': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'/': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'\\': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'(': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
')': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'[': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
']': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'{': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'}': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
':': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
';': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'"': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'\'': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
',': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'_': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'|': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
// Greek Letters
'π': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
// Currency Symbols
'€': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'£': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'¥': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
// Arrows
'←': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'→': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'↑': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'↓': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
// Special Symbols
'©': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'®': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
// Box Drawing
'┌': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'┐': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'└': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'┘': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
// Math Symbols
'×': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'÷': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'±': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
// Emoticons and Fun Symbols
'^': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'_': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'♥': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'☺': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'☹': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
'★': [
[],
[],
[],
[],
[],
[],
[],
[]
].map(row => row.map(Boolean)),
};
class BitmapFontGenerator {
constructor(options = {}) {
this.width = options.width || 8;
this.height = options.height || 8;
this.spacing = options.spacing || 1;
this.displayOptions = {
on: options.onChar || '█',
off: options.offChar || ' ',
color: options.color || '#ffffff',
backgroundColor: options.backgroundColor || 'transparent'
};
this.font = new Map();
this.originalPatterns = new Map(); // Store original patterns
}
/**
* Define a character in the bitmap font
* @param {string} char - Single character to define
* @param {boolean[][]} pattern - 2D array representing the bitmap pattern
*/
defineCharacter(char, pattern) {
// Store original pattern
this.originalPatterns.set(char.toUpperCase(), pattern);
// Scale pattern to current dimensions
const scaledPattern = this.scalePattern(pattern);
this.font.set(char.toUpperCase(), scaledPattern);
}
/**
* Scale a pattern to match current dimensions
* @param {boolean[][]} pattern - Original pattern
* @returns {boolean[][]} Scaled pattern
*/
scalePattern(pattern) {
const originalHeight = pattern.length;
const originalWidth = pattern[0].length;
const result = Array(this.height).fill().map(() => Array(this.width).fill(false));
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
// Map current coordinates to original pattern coordinates
const origX = Math.floor(x * originalWidth / this.width);
const origY = Math.floor(y * originalHeight / this.height);
result[y][x] = pattern[origY][origX];
}
}
return result;
}
/**
* Regenerate all font patterns with new dimensions
*/
regeneratePatterns() {
this.font.clear();
this.originalPatterns.forEach((pattern, char) => {
const scaledPattern = this.scalePattern(pattern);
this.font.set(char, scaledPattern);
});
}
/**
* Generate bitmap text
* @param {string} text - Text to render
* @returns {boolean[][]} - 2D array representing the bitmap text
*/
generateText(text) {
const upperText = text.toUpperCase();
const result = Array(this.height).fill().map(() => []);
for (let i = 0; i < upperText.length; i++) {
const char = upperText[i];
const pattern = this.font.get(char);
if (!pattern) continue;
// Add character pattern
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
result[y].push(pattern[y][x]);
}
// Add spacing between characters
if (i < upperText.length - 1) {
for (let s = 0; s < this.spacing; s++) {
result[y].push(false);
}
}
}
}
return result;
}
/**
* Convert bitmap to ASCII art
* @param {boolean[][]} bitmap - 2D array representing the bitmap
* @param {Object} options - Display options
* @returns {string} ASCII art representation
*/
toAscii(bitmap, options = {}) {
const displayChar = options.on || this.displayOptions.on;
const emptyChar = options.off || this.displayOptions.off;
return bitmap.map(row =>
row.map(pixel => pixel ? displayChar : emptyChar).join('')
).join('\n');
}
/**
* Convert bitmap to HTML
* @param {boolean[][]} bitmap - 2D array representing the bitmap
* @param {Object} options - Display options
* @returns {string} HTML representation
*/
toHtml(bitmap, options = {}) {
const color = options.color || this.displayOptions.color;
const backgroundColor = options.backgroundColor || this.displayOptions.backgroundColor;
const displayChar = options.on || this.displayOptions.on;
const emptyChar = options.off || this.displayOptions.off;
const asciiArt = this.toAscii(bitmap, { on: displayChar, off: emptyChar });
return `<pre style="color: ${color}; background-color: ${backgroundColor}; font-family: monospace;">${asciiArt}</pre>`;
}
/**
* Set the display character for filled pixels
* @param {string} char - Character to use for filled pixels
*/
setDisplayChar(char) {
this.displayOptions.on = char;
}
/**
* Set the display character for empty pixels
* @param {string} char - Character to use for empty pixels
*/
setEmptyChar(char) {
this.displayOptions.off = char;
}
/**
* Set the text color
* @param {string} color - CSS color value
*/
setColor(color) {
this.displayOptions.color = color;
}
/**
* Set the background color
* @param {string} color - CSS color value
*/
setBackgroundColor(color) {
this.displayOptions.backgroundColor = color;
}
/**
* Set the font dimensions
* @param {number} width - Width of each character
* @param {number} height - Height of each character
*/
setDimensions(width, height) {
if (width) this.width = width;
if (height) this.height = height;
this.regeneratePatterns();
}
/**
* Set the spacing between characters
* @param {number} spacing - Number of pixels between characters
*/
setSpacing(spacing) {
this.spacing = spacing;
}
/**
* Get current display options
* @returns {Object} Current display options
*/
getDisplayOptions() {
return { ...this.displayOptions };
}
/**
* Get current dimensions
* @returns {Object} Current width, height, and spacing
*/
getDimensions() {
return {
width: this.width,
height: this.height,
spacing: this.spacing
};
}
/**
* Reset display options to defaults
*/
resetDisplayOptions() {
this.displayOptions = {
on: '█',
off: ' ',
color: '#ffffff',
backgroundColor: 'transparent'
};
}
/**
* Create a styled text output
* @param {string} text - Text to render
* @param {Object} options - Display options
* @returns {string} Formatted text output
*/
createText(text, options = {}) {
const bitmap = this.generateText(text);
return options.html ? this.toHtml(bitmap, options) : this.toAscii(bitmap, options);
}
}
exports.BitmapFontGenerator = BitmapFontGenerator;
exports.defaultFont = defaultFont;
return exports;
})({});