diff --git a/PxPlus_IBM_VGA_8x16.ttf b/PxPlus_IBM_VGA_8x16.ttf new file mode 100644 index 0000000..3bf32d0 Binary files /dev/null and b/PxPlus_IBM_VGA_8x16.ttf differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..14ab9fd --- /dev/null +++ b/main.py @@ -0,0 +1,227 @@ +import sys +import os +import random +import json +from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit, QLabel, + QSpinBox, QComboBox, QColorDialog, QFileDialog, QGraphicsView, QGraphicsScene, + QGraphicsTextItem, QGraphicsRectItem, QSlider, QGraphicsItem) +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QColor, QFont, QImage, QPainter, QPixmap +from PIL import Image, ImageDraw, ImageFont + + +class FontConverter(QWidget): + def __init__(self): + super().__init__() + + # Default parameters + self.default_cell_width = 16 + self.default_cell_height = 16 + self.default_text_color = QColor(255, 255, 255) + self.default_bg_color = QColor(0, 0, 0) + self.default_format = "TIFF" + self.font_path = "PxPlus_IBM_VGA_8x16.ttf" # Ensure the font is available + + self.presets = self.load_presets() + self.preview_char = random.choice(self.presets.get('Latin', '')) + + self.init_ui() + + def load_presets(self): + try: + with open('presets.json', 'r', encoding='utf-8') as f: + data = json.load(f) + return data.get('Presets', {}) + except Exception as e: + print(f"Failed to load presets: {e}") + return {} + + def init_ui(self): + self.setWindowTitle("Font Converter") + self.setGeometry(100, 100, 600, 500) + + # Layouts + main_layout = QVBoxLayout() + settings_layout = QVBoxLayout() + + # Font selection + self.font_select_button = QPushButton("Select Font") + self.font_select_button.clicked.connect(self.select_font) + self.font_label = QLabel(f"Selected Font: {self.font_path}") + settings_layout.addWidget(self.font_label) + settings_layout.addWidget(self.font_select_button) + + # Cell size controls + settings_layout.addWidget(QLabel("Cell size")) + self.cell_width_spin = QSpinBox() + self.cell_width_spin.setValue(self.default_cell_width) + self.cell_width_spin.valueChanged.connect(self.update_preview) + self.cell_height_spin = QSpinBox() + self.cell_height_spin.setValue(self.default_cell_height) + self.cell_height_spin.valueChanged.connect(self.update_preview) + settings_layout.addWidget(QLabel("Width")) + settings_layout.addWidget(self.cell_width_spin) + settings_layout.addWidget(QLabel("Height")) + settings_layout.addWidget(self.cell_height_spin) + + # Color pickers + settings_layout.addWidget(QLabel("Text color")) + self.text_color_button = QPushButton("Select Text Color") + self.text_color_button.clicked.connect(self.select_text_color) + settings_layout.addWidget(self.text_color_button) + + settings_layout.addWidget(QLabel("Background color")) + self.bg_color_button = QPushButton("Select Background Color") + self.bg_color_button.clicked.connect(self.select_bg_color) + settings_layout.addWidget(self.bg_color_button) + + # Offset controls + settings_layout.addWidget(QLabel("Symbol offset")) + self.offset_x_slider = QSlider(Qt.Orientation.Horizontal) + self.offset_x_slider.setMinimum(0) + self.offset_x_slider.setMaximum(64) + self.offset_x_slider.setValue(0) + self.offset_x_slider.valueChanged.connect(self.update_preview) + self.offset_y_slider = QSlider(Qt.Orientation.Horizontal) + self.offset_y_slider.setMinimum(0) + self.offset_y_slider.setMaximum(64) + self.offset_y_slider.setValue(0) + self.offset_y_slider.valueChanged.connect(self.update_preview) + + settings_layout.addWidget(QLabel("Offset X")) + settings_layout.addWidget(self.offset_x_slider) + settings_layout.addWidget(QLabel("Offset Y")) + settings_layout.addWidget(self.offset_y_slider) + + # Preset selection + self.preset_combo = QComboBox() + self.preset_combo.addItems(self.presets.keys()) + self.preset_combo.currentTextChanged.connect(self.update_symbols_from_preset) + settings_layout.addWidget(QLabel("Select Preset")) + settings_layout.addWidget(self.preset_combo) + + # Symbol range input + self.symbol_input = QLineEdit(self.preview_char) + self.symbol_input.setFixedHeight(80) + settings_layout.addWidget(self.symbol_input) + + # Format selection + self.format_combo = QComboBox() + self.format_combo.addItems(["TIFF", "PNG", "BMP"]) + self.format_combo.setCurrentText(self.default_format) + settings_layout.addWidget(QLabel("Image Format")) + settings_layout.addWidget(self.format_combo) + + # Output folder input + self.output_folder_input = QLineEdit() + self.output_folder_button = QPushButton("Select Output Folder") + self.output_folder_button.clicked.connect(self.select_output_folder) + folder_layout = QHBoxLayout() + folder_layout.addWidget(self.output_folder_input) + folder_layout.addWidget(self.output_folder_button) + settings_layout.addLayout(folder_layout) + + # Generate button + self.generate_button = QPushButton("Generate Images") + self.generate_button.clicked.connect(self.generate_images) + settings_layout.addWidget(self.generate_button) + + # Preview + self.preview_scene = QGraphicsScene() + self.preview_view = QGraphicsView(self.preview_scene) + settings_layout.addWidget(self.preview_view) + + main_layout.addLayout(settings_layout) + self.setLayout(main_layout) + + self.update_preview() + + def update_preview(self): + # Clear preview scene + self.preview_scene.clear() + + # Get current parameters + width = self.cell_width_spin.value() + height = self.cell_height_spin.value() + offset_x = self.offset_x_slider.value() + offset_y = self.offset_y_slider.value() + + # Create background rectangle + bg_color = self.default_bg_color + self.preview_scene.addItem(QGraphicsRectItem(0, 0, width, height).setBrush(bg_color)) + + # Add preview character + preview_text_item = QGraphicsTextItem(self.preview_char) + preview_text_item.setPos(offset_x, offset_y) + preview_text_item.setDefaultTextColor(self.default_text_color) + preview_text_item.setFont(QFont(self.font_path, height - 2)) # Adjust font size + self.preview_scene.addItem(preview_text_item) + + def update_symbols_from_preset(self, preset_name): + # Update symbol input field based on selected preset + symbols = self.presets.get(preset_name, "") + self.symbol_input.setText(symbols) + self.preview_char = random.choice(symbols) + self.update_preview() + + def select_font(self): + font, _ = QFileDialog.getOpenFileName(self, "Select Font", "", "Font Files (*.ttf *.otf)") + if font: + self.font_path = font + self.font_label.setText(f"Selected Font: {font}") + self.update_preview() + + def select_text_color(self): + color = QColorDialog.getColor() + if color.isValid(): + self.default_text_color = color + self.update_preview() + + def select_bg_color(self): + color = QColorDialog.getColor() + if color.isValid(): + self.default_bg_color = color + self.update_preview() + + def select_output_folder(self): + folder = QFileDialog.getExistingDirectory(self, "Select Output Folder") + if folder: + self.output_folder_input.setText(folder) + + def generate_images(self): + width = self.cell_width_spin.value() + height = self.cell_height_spin.value() + offset_x = self.offset_x_slider.value() + offset_y = self.offset_y_slider.value() + text_color = self.default_text_color + bg_color = self.default_bg_color + symbols = self.symbol_input.text() + output_folder = self.output_folder_input.text() + image_format = self.format_combo.currentText() + + if not os.path.exists(output_folder): + print("Output folder does not exist!") + return + + try: + font = ImageFont.truetype(self.font_path, height - 2) + except Exception as e: + print(f"Failed to load font: {e}") + return + + for char in symbols: + if char.isspace(): + continue + img = Image.new("RGBA", (width, height), bg_color.getRgb()[:3]) + draw = ImageDraw.Draw(img) + draw.text((offset_x, offset_y), char, font=font, fill=text_color.getRgb()[:3]) + safe_char = f"U+{ord(char):04X}" + img.save(os.path.join(output_folder, f"{safe_char}.{image_format.lower()}"), format=image_format) + print("Images successfully generated.") + + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = FontConverter() + window.show() + sys.exit(app.exec()) diff --git a/presets.json b/presets.json new file mode 100644 index 0000000..bd9ed26 --- /dev/null +++ b/presets.json @@ -0,0 +1,11 @@ +{ + "Presets": { + "Latin": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "Latin+Specals": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()_+-={}[]|\\:;\"'<>,.?/~`", + "Cyrillic": "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯаабвгдеёжзийклмнопрстуфхцчшщыэюя", + "Cyrillic+Specials": "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯаабвгдеёжзийклмнопрстуфхцчшщыэюя!@#$%^&*()_+-={}[]|\\:;\"'<>,.?/~`", + "Latin+Cyrillic+Specials": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯаабвгдеёжзийклмнопрстуфхцчшщыэюя!@#$%^&*()_+-={}[]|\\:;\"'<>,.?/~`", + "Specials": "!@#$%^&*()_+-={}[]|\\:;\"'<>,.?/~`", + "All Characters": "under construction" + } +}