- KPIs complets (CA, Heures, Taux, Stats, Objectifs) - Graphiques CA par client + Heures par client - TCD Heures x Client x Projet avec Slicer interactif - Checkboxes dynamiques bien positionnes (E3:E7) - Documentation technique et guides MCP Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
144 lines
4.8 KiB
Python
144 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Simple Markdown to PDF converter using markdown2 and reportlab
|
|
"""
|
|
import markdown2
|
|
from reportlab.lib.pagesizes import letter, A4
|
|
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
|
from reportlab.lib.units import inch
|
|
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table, TableStyle
|
|
from reportlab.lib import colors
|
|
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
|
|
from html.parser import HTMLParser
|
|
import re
|
|
|
|
class MarkdownToPDFConverter:
|
|
def __init__(self, input_file, output_file):
|
|
self.input_file = input_file
|
|
self.output_file = output_file
|
|
self.doc = SimpleDocTemplate(
|
|
output_file,
|
|
pagesize=A4,
|
|
rightMargin=72,
|
|
leftMargin=72,
|
|
topMargin=72,
|
|
bottomMargin=18
|
|
)
|
|
self.styles = getSampleStyleSheet()
|
|
self._setup_styles()
|
|
self.story = []
|
|
|
|
def _setup_styles(self):
|
|
"""Setup custom styles"""
|
|
# Title style
|
|
self.styles.add(ParagraphStyle(
|
|
name='CustomTitle',
|
|
parent=self.styles['Heading1'],
|
|
fontSize=24,
|
|
textColor=colors.HexColor('#2C3E50'),
|
|
spaceAfter=30,
|
|
alignment=TA_CENTER
|
|
))
|
|
|
|
# Heading 2
|
|
self.styles.add(ParagraphStyle(
|
|
name='CustomHeading2',
|
|
parent=self.styles['Heading2'],
|
|
fontSize=18,
|
|
textColor=colors.HexColor('#2C3E50'),
|
|
spaceAfter=12,
|
|
spaceBefore=12
|
|
))
|
|
|
|
# Heading 3
|
|
self.styles.add(ParagraphStyle(
|
|
name='CustomHeading3',
|
|
parent=self.styles['Heading3'],
|
|
fontSize=14,
|
|
textColor=colors.HexColor('#27AE60'),
|
|
spaceAfter=10,
|
|
spaceBefore=10
|
|
))
|
|
|
|
# Code style
|
|
self.styles.add(ParagraphStyle(
|
|
name='Code',
|
|
parent=self.styles['Normal'],
|
|
fontSize=8,
|
|
textColor=colors.black,
|
|
backColor=colors.HexColor('#ECF0F1'),
|
|
leftIndent=20,
|
|
rightIndent=20
|
|
))
|
|
|
|
def convert(self):
|
|
"""Convert markdown file to PDF"""
|
|
# Read markdown file
|
|
with open(self.input_file, 'r', encoding='utf-8') as f:
|
|
md_content = f.read()
|
|
|
|
# Convert markdown to HTML
|
|
html = markdown2.markdown(md_content, extras=['tables', 'fenced-code-blocks', 'header-ids'])
|
|
|
|
# Parse HTML and convert to PDF elements
|
|
self._parse_html_to_pdf(html)
|
|
|
|
# Build PDF
|
|
self.doc.build(self.story)
|
|
|
|
def _parse_html_to_pdf(self, html):
|
|
"""Parse HTML and convert to PDF elements"""
|
|
# Split by major sections
|
|
lines = html.split('\n')
|
|
|
|
for line in lines:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
|
|
# Handle headings
|
|
if line.startswith('<h1'):
|
|
text = re.sub(r'<.*?>', '', line)
|
|
self.story.append(Paragraph(text, self.styles['CustomTitle']))
|
|
self.story.append(Spacer(1, 0.2*inch))
|
|
|
|
elif line.startswith('<h2'):
|
|
text = re.sub(r'<.*?>', '', line)
|
|
self.story.append(Spacer(1, 0.2*inch))
|
|
self.story.append(Paragraph(text, self.styles['CustomHeading2']))
|
|
self.story.append(Spacer(1, 0.1*inch))
|
|
|
|
elif line.startswith('<h3'):
|
|
text = re.sub(r'<.*?>', '', line)
|
|
self.story.append(Paragraph(text, self.styles['CustomHeading3']))
|
|
self.story.append(Spacer(1, 0.1*inch))
|
|
|
|
elif line.startswith('<p>'):
|
|
text = re.sub(r'<.*?>', '', line)
|
|
if text:
|
|
self.story.append(Paragraph(text, self.styles['Normal']))
|
|
self.story.append(Spacer(1, 0.1*inch))
|
|
|
|
elif line.startswith('<code>') or line.startswith('<pre>'):
|
|
text = re.sub(r'<.*?>', '', line)
|
|
if text:
|
|
self.story.append(Paragraph(text, self.styles['Code']))
|
|
self.story.append(Spacer(1, 0.1*inch))
|
|
|
|
elif line.startswith('<li>'):
|
|
text = re.sub(r'<.*?>', '', line)
|
|
if text:
|
|
self.story.append(Paragraph(f"• {text}", self.styles['Normal']))
|
|
|
|
elif line.startswith('<hr'):
|
|
self.story.append(Spacer(1, 0.2*inch))
|
|
self.story.append(PageBreak())
|
|
|
|
if __name__ == '__main__':
|
|
input_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.md'
|
|
output_file = r'C:\Users\alexi\Documents\projects\freelance-dashboard\TECHNICAL_REFERENCE_EN.pdf'
|
|
|
|
converter = MarkdownToPDFConverter(input_file, output_file)
|
|
converter.convert()
|
|
print(f"PDF created successfully: {output_file}")
|