#!/usr/bin/env python3
"""
PowerON Markdown to HTML Converter
Konvertiert Markdown-Dateien zu HTML mit PowerON-Styling
"""
import os
import sys
import argparse
from pathlib import Path
import re
# Einfache Markdown-zu-HTML Konvertierung ohne externe Abhängigkeiten
def _convert_markdown_to_html(markdown_text):
"""Einfache Markdown-zu-HTML Konvertierung ohne externe Bibliotheken"""
# HTML-Escaping
def escape_html(text):
return (text.replace('&', '&')
.replace('<', '<')
.replace('>', '>')
.replace('"', '"')
.replace("'", '''))
# Code-Blöcke zuerst verarbeiten (vor anderen Formatierungen)
def process_code_blocks(text):
# Fenced code blocks (```)
def replace_code_block(match):
language = match.group(1) or ''
code = match.group(2)
return f'
{escape_html(code)}
'
text = re.sub(r'```(\w+)?\n(.*?)\n```', replace_code_block, text, flags=re.DOTALL)
# Inline code (`)
text = re.sub(r'`([^`]+)`', r'\1', text)
return text
# Headers
def process_headers(text):
text = re.sub(r'^###### (.*?)$', r'
\1
', text, flags=re.MULTILINE)
text = re.sub(r'^##### (.*?)$', r'
\1
', text, flags=re.MULTILINE)
text = re.sub(r'^#### (.*?)$', r'
\1
', text, flags=re.MULTILINE)
text = re.sub(r'^### (.*?)$', r'
\1
', text, flags=re.MULTILINE)
text = re.sub(r'^## (.*?)$', r'
\1
', text, flags=re.MULTILINE)
text = re.sub(r'^# (.*?)$', r'
\1
', text, flags=re.MULTILINE)
return text
# Listen
def process_lists(text):
lines = text.split('\n')
in_ul = False
in_ol = False
result = []
for line in lines:
# Ungeordnete Listen
if re.match(r'^\s*[-*+]\s+', line):
if in_ol:
result.append('')
in_ol = False
if not in_ul:
result.append('
')
# Geordnete Listen
elif re.match(r'^\s*\d+\.\s+', line):
if in_ul:
result.append('
')
in_ul = False
if not in_ol:
result.append('')
in_ol = True
item = re.sub(r'^\s*\d+\.\s+', '', line)
result.append(f'
{item}
')
else:
if in_ul:
result.append('')
in_ul = False
if in_ol:
result.append('')
in_ol = False
result.append(line)
if in_ul:
result.append('')
if in_ol:
result.append('')
return '\n'.join(result)
# Links und Bilder
def process_links_and_images(text):
# Bilder 
text = re.sub(r'!\[([^\]]*)\]\(([^)]+)\)', r'', text)
# Links [text](url)
text = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'\1', text)
return text
# Bold und Italic
def process_emphasis(text):
# Bold **text**
text = re.sub(r'\*\*(.*?)\*\*', r'\1', text)
# Italic *text*
text = re.sub(r'\*(.*?)\*', r'\1', text)
return text
# Blockquotes
def process_blockquotes(text):
lines = text.split('\n')
in_quote = False
result = []
for line in lines:
if line.strip().startswith('>'):
if not in_quote:
result.append('
')
in_quote = False
result.append(line)
if in_quote:
result.append('')
return '\n'.join(result)
# Tabellen
def process_tables(text):
lines = text.split('\n')
result = []
i = 0
while i < len(lines):
line = lines[i].strip()
# Prüfe ob es eine Tabellenzeile ist (enthält |)
if '|' in line and not line.startswith('<'):
table_lines = []
j = i
# Sammle alle aufeinanderfolgenden Tabellenzeilen
while j < len(lines) and '|' in lines[j].strip() and not lines[j].strip().startswith('<'):
table_lines.append(lines[j].strip())
j += 1
if len(table_lines) >= 2: # Mindestens Header + Separator
# Erstelle HTML-Tabelle
table_html = ['
']
# Header-Zeile
header_cells = [cell.strip() for cell in table_lines[0].split('|')[1:-1]]
table_html.append('
')
for cell in header_cells:
table_html.append(f'
{cell}
')
table_html.append('
')
# Separator-Zeile überspringen
if len(table_lines) > 1 and '---' in table_lines[1]:
data_start = 2
else:
data_start = 1
# Daten-Zeilen
if len(table_lines) > data_start:
table_html.append('')
for row in table_lines[data_start:]:
if '|' in row:
data_cells = [cell.strip() for cell in row.split('|')[1:-1]]
table_html.append('
')
for cell in data_cells:
table_html.append(f'
{cell}
')
table_html.append('
')
table_html.append('')
table_html.append('
')
result.append('\n'.join(table_html))
i = j - 1 # -1 weil i am Ende des Loops erhöht wird
else:
result.append(f'
{line}
')
else:
result.append(f'
{line}
')
i += 1
return '\n'.join(result)
# Horizontale Linien
def process_hr(text):
text = re.sub(r'^---$', '', text, flags=re.MULTILINE)
return text
# Paragraphen - Jeder Zeilenumbruch wird zu einem
Tag
def process_paragraphs(text):
lines = text.split('\n')
result = []
for line in lines:
line = line.strip()
if line:
# Nur wenn es nicht schon ein HTML-Tag ist
if not re.match(r'<[h1-6]|
{line}')
else:
result.append(line)
else:
# Leere Zeilen werden zu leeren
Tags
result.append('
')
return '\n'.join(result)
# Verarbeitung in der richtigen Reihenfolge
text = markdown_text
# Code-Blöcke zuerst (vor anderen Formatierungen)
text = process_code_blocks(text)
# Headers
text = process_headers(text)
# Tabellen
text = process_tables(text)
# Blockquotes
text = process_blockquotes(text)
# Listen
text = process_lists(text)
# Links und Bilder
text = process_links_and_images(text)
# Emphasis
text = process_emphasis(text)
# Horizontale Linien
text = process_hr(text)
# Paragraphen
text = process_paragraphs(text)
return text
class PowerONHTMLConverter:
def __init__(self, css_file="poweron-styles.css"):
self.css_file = css_file
def create_html_template(self, title, content, css_path=None):
"""Erstellt HTML-Template mit PowerON-Styling"""
if css_path is None:
css_path = self.css_file
html_template = f"""
{title} | PowerON
{content}
"""
return html_template
def convert_markdown_file(self, input_file, output_file=None, css_path=None):
"""Konvertiert eine Markdown-Datei zu HTML"""
input_path = Path(input_file)
if not input_path.exists():
raise FileNotFoundError(f"Markdown-Datei nicht gefunden: {input_file}")
# Output-Datei bestimmen
if output_file is None:
output_file = input_path.with_suffix('.html')
else:
output_file = Path(output_file)
# Markdown lesen und konvertieren
with open(input_path, 'r', encoding='utf-8') as f:
markdown_content = f.read()
# Titel aus Markdown extrahieren (erste H1)
title_match = re.search(r'^#\s+(.+)$', markdown_content, re.MULTILINE)
title = title_match.group(1) if title_match else input_path.stem
# Markdown zu HTML konvertieren
html_content = _convert_markdown_to_html(markdown_content)
# HTML-Template erstellen
full_html = self.create_html_template(title, html_content, css_path)
# HTML-Datei schreiben
with open(output_file, 'w', encoding='utf-8') as f:
f.write(full_html)
print(f"Konvertiert: {input_file} -> {output_file}")
return output_file
def convert_directory(self, input_dir, output_dir=None, css_path=None):
"""Konvertiert alle Markdown-Dateien in einem Verzeichnis"""
input_path = Path(input_dir)
if not input_path.exists():
raise FileNotFoundError(f"Verzeichnis nicht gefunden: {input_dir}")
if output_dir is None:
output_dir = input_path / "html_output"
else:
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True)
# Alle .md Dateien finden
md_files = list(input_path.glob("**/*.md"))
if not md_files:
print(f"⚠️ Keine Markdown-Dateien gefunden in: {input_dir}")
return
print(f"📁 Konvertiere {len(md_files)} Markdown-Dateien...")
for md_file in md_files:
# Relativen Pfad beibehalten
relative_path = md_file.relative_to(input_path)
output_file = output_dir / relative_path.with_suffix('.html')
# Verzeichnis erstellen falls nötig
output_file.parent.mkdir(parents=True, exist_ok=True)
try:
self.convert_markdown_file(md_file, output_file, css_path)
except Exception as e:
print(f"❌ Fehler bei {md_file}: {e}")
def main():
parser = argparse.ArgumentParser(
description="PowerON Markdown zu HTML Konverter",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Beispiele:
python md_to_html_converter.py document.md
python md_to_html_converter.py document.md -o output.html
python md_to_html_converter.py -d ./docs -o ./html_output
python md_to_html_converter.py document.md -c ../styles/custom.css
"""
)
parser.add_argument('input', nargs='?', help='Markdown-Datei oder Verzeichnis')
parser.add_argument('-o', '--output', help='Output-Datei oder -verzeichnis')
parser.add_argument('-d', '--directory', help='Verzeichnis mit Markdown-Dateien konvertieren')
parser.add_argument('-c', '--css', default='poweron-styles.css', help='CSS-Datei (Standard: poweron-styles.css)')
parser.add_argument('--version', action='version', version='PowerON MD to HTML Converter 1.0')
args = parser.parse_args()
if not args.input and not args.directory:
parser.print_help()
return
try:
converter = PowerONHTMLConverter(args.css)
if args.directory:
converter.convert_directory(args.directory, args.output, args.css)
else:
converter.convert_markdown_file(args.input, args.output, args.css)
print("Konvertierung abgeschlossen!")
except Exception as e:
print(f"Fehler: {e}")
sys.exit(1)
if __name__ == "__main__":
main()