305 lines
9.9 KiB
Python
305 lines
9.9 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Konvertiert die IHK-Projektdokumentation von Markdown nach Word (DOCX)
|
||
mit IHK-konformen Formatierungen.
|
||
"""
|
||
|
||
import os
|
||
import re
|
||
from docx import Document
|
||
from docx.shared import Pt, Inches, RGBColor
|
||
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
|
||
from docx.enum.style import WD_STYLE_TYPE
|
||
from markdown import markdown
|
||
from bs4 import BeautifulSoup
|
||
import html2text
|
||
|
||
def create_ihk_styles(doc):
|
||
"""Erstellt IHK-konforme Formatvorlagen"""
|
||
|
||
# Normaler Text
|
||
normal_style = doc.styles['Normal']
|
||
normal_style.font.name = 'Arial'
|
||
normal_style.font.size = Pt(11)
|
||
normal_style.paragraph_format.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE
|
||
normal_style.paragraph_format.space_after = Pt(6)
|
||
normal_style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
|
||
|
||
# Überschrift 1
|
||
h1_style = doc.styles['Heading 1']
|
||
h1_style.font.name = 'Arial'
|
||
h1_style.font.size = Pt(16)
|
||
h1_style.font.bold = True
|
||
h1_style.paragraph_format.space_before = Pt(12)
|
||
h1_style.paragraph_format.space_after = Pt(12)
|
||
h1_style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
||
|
||
# Überschrift 2
|
||
h2_style = doc.styles['Heading 2']
|
||
h2_style.font.name = 'Arial'
|
||
h2_style.font.size = Pt(14)
|
||
h2_style.font.bold = True
|
||
h2_style.paragraph_format.space_before = Pt(12)
|
||
h2_style.paragraph_format.space_after = Pt(6)
|
||
h2_style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
||
|
||
# Überschrift 3
|
||
h3_style = doc.styles['Heading 3']
|
||
h3_style.font.name = 'Arial'
|
||
h3_style.font.size = Pt(12)
|
||
h3_style.font.bold = True
|
||
h3_style.paragraph_format.space_before = Pt(6)
|
||
h3_style.paragraph_format.space_after = Pt(6)
|
||
h3_style.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
||
|
||
# Code-Style
|
||
try:
|
||
code_style = doc.styles.add_style('Code', WD_STYLE_TYPE.CHARACTER)
|
||
code_style.font.name = 'Courier New'
|
||
code_style.font.size = Pt(10)
|
||
except:
|
||
code_style = doc.styles['Code']
|
||
|
||
return doc
|
||
|
||
def setup_document_layout(doc):
|
||
"""Richtet das Dokumentlayout nach IHK-Vorgaben ein"""
|
||
sections = doc.sections
|
||
for section in sections:
|
||
# Seitenränder (IHK-Standard)
|
||
section.top_margin = Inches(1.0)
|
||
section.bottom_margin = Inches(1.0)
|
||
section.left_margin = Inches(1.25)
|
||
section.right_margin = Inches(1.0)
|
||
|
||
# Seitengröße A4
|
||
section.page_height = Inches(11.69)
|
||
section.page_width = Inches(8.27)
|
||
|
||
def add_title_page(doc):
|
||
"""Fügt die Titelseite hinzu"""
|
||
# Titel
|
||
p = doc.add_paragraph()
|
||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
p.add_run('Abschlussprüfung - Sommer 2025\n').bold = True
|
||
p.add_run('\n')
|
||
|
||
p = doc.add_paragraph()
|
||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
p.add_run('Fachinformatiker für digitale Vernetzung\n').font.size = Pt(14)
|
||
|
||
doc.add_paragraph()
|
||
|
||
p = doc.add_paragraph()
|
||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
p.add_run('Dokumentation der betrieblichen Projektarbeit\n').font.size = Pt(16)
|
||
|
||
doc.add_paragraph()
|
||
|
||
# Projekttitel
|
||
p = doc.add_paragraph()
|
||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
run = p.add_run('MYP – Manage Your Printer\n')
|
||
run.font.size = Pt(18)
|
||
run.font.bold = True
|
||
|
||
p = doc.add_paragraph()
|
||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
p.add_run('Digitalisierung des 3D-Drucker-Reservierungsprozesses durch Etablierung\n')
|
||
p.add_run('der cyberphysischen Kommunikation mit relevanten Hardwarekomponenten')
|
||
|
||
# Mehrere Leerzeilen
|
||
for _ in range(5):
|
||
doc.add_paragraph()
|
||
|
||
# Abgabedatum
|
||
p = doc.add_paragraph()
|
||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
p.add_run('Abgabedatum: 5. Juni 2025').bold = True
|
||
|
||
doc.add_paragraph()
|
||
doc.add_paragraph()
|
||
|
||
# Ausbildungsbetrieb
|
||
p = doc.add_paragraph()
|
||
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
||
p.add_run('Ausbildungsbetrieb\n').bold = True
|
||
p.add_run('\n')
|
||
p.add_run('Mercedes-Benz AG\n')
|
||
p.add_run('Daimlerstraße 143\n')
|
||
p.add_run('D-12277 Berlin')
|
||
|
||
doc.add_paragraph()
|
||
|
||
# Prüfungsbewerber
|
||
p = doc.add_paragraph()
|
||
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
||
p.add_run('Prüfungsbewerber\n').bold = True
|
||
p.add_run('\n')
|
||
p.add_run('Till Tomczak\n')
|
||
p.add_run('Hainbuchenstraße 19\n')
|
||
p.add_run('D-16761 Hennigsdorf')
|
||
|
||
# Seitenumbruch nach Titelseite
|
||
doc.add_page_break()
|
||
|
||
def process_markdown_content(content):
|
||
"""Verarbeitet Markdown-Inhalt und strukturiert ihn für Word"""
|
||
# Entferne Bilder vorerst
|
||
content = re.sub(r'<img[^>]*>', '', content)
|
||
|
||
# Teile den Inhalt in Abschnitte
|
||
lines = content.split('\n')
|
||
processed_content = []
|
||
|
||
skip_until_content = False
|
||
for line in lines:
|
||
# Skip Titelbereich
|
||
if line.strip().startswith('# Inhaltsverzeichnis'):
|
||
skip_until_content = True
|
||
continue
|
||
|
||
if skip_until_content and line.strip().startswith('# 1. Einleitung'):
|
||
skip_until_content = False
|
||
|
||
if not skip_until_content and not line.strip().startswith('Mercedes-Benz') and \
|
||
not line.strip().startswith('Till Tomczak') and \
|
||
not line.strip().startswith('Abgabedatum:'):
|
||
processed_content.append(line)
|
||
|
||
return '\n'.join(processed_content)
|
||
|
||
def add_content_to_document(doc, content):
|
||
"""Fügt den Inhalt zum Word-Dokument hinzu"""
|
||
lines = content.split('\n')
|
||
current_paragraph = None
|
||
in_code_block = False
|
||
|
||
for line in lines:
|
||
# Überschrift 1
|
||
if line.startswith('# '):
|
||
heading = line[2:].strip()
|
||
doc.add_heading(heading, level=1)
|
||
current_paragraph = None
|
||
|
||
# Überschrift 2
|
||
elif line.startswith('## '):
|
||
heading = line[3:].strip()
|
||
doc.add_heading(heading, level=2)
|
||
current_paragraph = None
|
||
|
||
# Überschrift 3
|
||
elif line.startswith('### '):
|
||
heading = line[4:].strip()
|
||
doc.add_heading(heading, level=3)
|
||
current_paragraph = None
|
||
|
||
# Überschrift 4
|
||
elif line.startswith('#### '):
|
||
heading = line[5:].strip()
|
||
# Word hat standardmäßig nur 3 Heading-Ebenen, nutze fetten Text
|
||
p = doc.add_paragraph()
|
||
p.add_run(heading).bold = True
|
||
current_paragraph = None
|
||
|
||
# Aufzählungen
|
||
elif line.strip().startswith('- '):
|
||
text = line.strip()[2:]
|
||
p = doc.add_paragraph(text, style='List Bullet')
|
||
current_paragraph = None
|
||
|
||
# Normaler Text
|
||
elif line.strip():
|
||
if current_paragraph is None:
|
||
current_paragraph = doc.add_paragraph()
|
||
else:
|
||
current_paragraph.add_run(' ')
|
||
|
||
# Verarbeite Inline-Formatierungen
|
||
process_inline_formatting(current_paragraph, line)
|
||
|
||
# Leerzeile
|
||
else:
|
||
current_paragraph = None
|
||
|
||
def process_inline_formatting(paragraph, text):
|
||
"""Verarbeitet Inline-Formatierungen wie fett und kursiv"""
|
||
# Ersetze Markdown-Formatierungen
|
||
parts = re.split(r'(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)', text)
|
||
|
||
for part in parts:
|
||
if part.startswith('**') and part.endswith('**'):
|
||
# Fett
|
||
paragraph.add_run(part[2:-2]).bold = True
|
||
elif part.startswith('*') and part.endswith('*') and not part.startswith('**'):
|
||
# Kursiv
|
||
paragraph.add_run(part[1:-1]).italic = True
|
||
elif part.startswith('`') and part.endswith('`'):
|
||
# Code
|
||
run = paragraph.add_run(part[1:-1])
|
||
run.font.name = 'Courier New'
|
||
run.font.size = Pt(10)
|
||
else:
|
||
# Normaler Text
|
||
paragraph.add_run(part)
|
||
|
||
def add_table_of_contents(doc):
|
||
"""Fügt ein Inhaltsverzeichnis hinzu"""
|
||
doc.add_heading('Inhaltsverzeichnis', level=1)
|
||
|
||
# Platzhalter für automatisches Inhaltsverzeichnis
|
||
p = doc.add_paragraph()
|
||
p.add_run('[Das Inhaltsverzeichnis wird in Word automatisch generiert.\n')
|
||
p.add_run('Verwenden Sie: Verweise → Inhaltsverzeichnis → Automatisches Verzeichnis]')
|
||
p.italic = True
|
||
|
||
doc.add_page_break()
|
||
|
||
def main():
|
||
"""Hauptfunktion"""
|
||
# Pfade
|
||
input_file = 'Dokumentation_Final_Markdown/Dokumentation.md'
|
||
output_file = 'IHK_Projektdokumentation_Final.docx'
|
||
|
||
# Lese Markdown-Datei
|
||
print("Lese Markdown-Datei...")
|
||
with open(input_file, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
# Erstelle Word-Dokument
|
||
print("Erstelle Word-Dokument...")
|
||
doc = Document()
|
||
|
||
# Richte Styles und Layout ein
|
||
print("Konfiguriere IHK-konforme Formatierung...")
|
||
create_ihk_styles(doc)
|
||
setup_document_layout(doc)
|
||
|
||
# Füge Titelseite hinzu
|
||
print("Erstelle Titelseite...")
|
||
add_title_page(doc)
|
||
|
||
# Füge Inhaltsverzeichnis hinzu
|
||
print("Füge Inhaltsverzeichnis hinzu...")
|
||
add_table_of_contents(doc)
|
||
|
||
# Verarbeite und füge Hauptinhalt hinzu
|
||
print("Verarbeite Dokumentinhalt...")
|
||
processed_content = process_markdown_content(content)
|
||
add_content_to_document(doc, processed_content)
|
||
|
||
# Speichere Dokument
|
||
print(f"Speichere Dokument als {output_file}...")
|
||
doc.save(output_file)
|
||
|
||
print("Konvertierung abgeschlossen!")
|
||
print("\nHinweise zur Nachbearbeitung:")
|
||
print("1. Überprüfen Sie die Formatierung und passen Sie sie ggf. an")
|
||
print("2. Generieren Sie das Inhaltsverzeichnis neu (Verweise → Inhaltsverzeichnis aktualisieren)")
|
||
print("3. Fügen Sie Kopf- und Fußzeilen mit Seitenzahlen hinzu")
|
||
print("4. Überprüfen Sie die Seitenumbrüche")
|
||
print("5. Fügen Sie ggf. Abbildungen und Diagramme ein")
|
||
|
||
if __name__ == "__main__":
|
||
main() |