223 lines
4.8 KiB
Python
223 lines
4.8 KiB
Python
import os
|
|
import markdown
|
|
from xhtml2pdf import pisa
|
|
from datetime import datetime
|
|
import io
|
|
|
|
# iT Guys Identity Colors
|
|
CSS_STYLES = """
|
|
@page {
|
|
size: A4;
|
|
margin: 20mm;
|
|
margin-bottom: 25mm; /* Space for footer */
|
|
background-color: #ffffff;
|
|
|
|
@frame footer_frame {
|
|
-pdf-frame-content: footer_content;
|
|
bottom: 10mm;
|
|
margin-left: 20mm;
|
|
margin-right: 20mm;
|
|
height: 10mm;
|
|
}
|
|
}
|
|
|
|
body {
|
|
font-family: Helvetica, Arial, sans-serif;
|
|
color: #333;
|
|
line-height: 1.5;
|
|
font-size: 11pt;
|
|
}
|
|
|
|
/* Headers */
|
|
h1 {
|
|
color: #1478cf; /* Primary Blue */
|
|
border-left: 5px solid #2ecc71; /* Green Accent */
|
|
padding-left: 15px;
|
|
background-color: #f0f8ff; /* Light Blue BG */
|
|
padding-top: 10px;
|
|
padding-bottom: 10px;
|
|
margin-top: 20px;
|
|
font-size: 24pt;
|
|
}
|
|
|
|
h2 {
|
|
color: #14508c; /* Darker Blue */
|
|
border-bottom: 2px solid #00f7ff; /* Cyan Accent */
|
|
padding-bottom: 5px;
|
|
margin-top: 30px;
|
|
font-size: 16pt;
|
|
}
|
|
|
|
h3 {
|
|
color: #444;
|
|
font-size: 13pt;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
/* Tables */
|
|
table {
|
|
width: 100%;
|
|
border: 1px solid #ddd;
|
|
margin: 20px 0;
|
|
font-size: 10pt;
|
|
}
|
|
|
|
th {
|
|
background-color: #1478cf;
|
|
color: white;
|
|
padding: 8px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
td {
|
|
border: 1px solid #ddd;
|
|
padding: 8px;
|
|
}
|
|
|
|
/* Callouts / Admonitions (Python-Markdown Extension) */
|
|
.admonition {
|
|
margin: 20px 0;
|
|
padding: 0;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
background-color: #ffffff;
|
|
page-break-inside: avoid;
|
|
}
|
|
|
|
.admonition-title {
|
|
font-weight: bold;
|
|
padding: 8px 12px;
|
|
background-color: #f0f0f0;
|
|
border-bottom: 1px solid #ddd;
|
|
font-size: 10pt;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* Specific Types */
|
|
.admonition.note, .admonition.info {
|
|
border-color: #bce8f1;
|
|
}
|
|
.admonition.note .admonition-title, .admonition.info .admonition-title {
|
|
background-color: #d9edf7;
|
|
color: #31708f;
|
|
}
|
|
|
|
.admonition.warning, .admonition.important, .admonition.attention {
|
|
border-color: #faebcc;
|
|
}
|
|
.admonition.warning .admonition-title, .admonition.important .admonition-title {
|
|
background-color: #fcf8e3;
|
|
color: #8a6d3b;
|
|
}
|
|
|
|
.admonition.tip, .admonition.hint, .admonition.success {
|
|
border-color: #d6e9c6;
|
|
}
|
|
.admonition.tip .admonition-title, .admonition.hint .admonition-title {
|
|
background-color: #dff0d8;
|
|
color: #3c763d;
|
|
}
|
|
|
|
.admonition p {
|
|
padding: 10px 12px;
|
|
margin: 0;
|
|
}
|
|
|
|
/* Legacy Blockquote fallback */
|
|
blockquote {
|
|
background-color: #f9f9f9;
|
|
border-left: 5px solid #ccc;
|
|
margin: 10px 0;
|
|
padding: 10px;
|
|
color: #666;
|
|
}
|
|
|
|
/* Code Blocks */
|
|
pre {
|
|
background-color: #2b2b2b;
|
|
color: #f8f8f2;
|
|
padding: 10px;
|
|
border: 1px solid #444;
|
|
font-family: monospace;
|
|
font-size: 9pt;
|
|
white-space: pre-wrap; /* Wrap long lines */
|
|
}
|
|
|
|
/* Images */
|
|
img {
|
|
zoom: 60%; /* xhtml2pdf sometimes renders images very large */
|
|
margin: 20px auto;
|
|
}
|
|
"""
|
|
|
|
def convert_markdown_to_pdf(input_path, output_path):
|
|
# Read Markdown
|
|
with open(input_path, 'r', encoding='utf-8') as f:
|
|
md_text = f.read()
|
|
|
|
# Process Variables
|
|
now = datetime.now()
|
|
replacements = {
|
|
'{{DATA_ATUAL}}': now.strftime("%d/%m/%Y"),
|
|
'{{ANO}}': str(now.year)
|
|
}
|
|
for k, v in replacements.items():
|
|
if k in md_text:
|
|
md_text = md_text.replace(k, v)
|
|
|
|
# Determine Base Directory for assets
|
|
base_dir = os.path.dirname(os.path.abspath(input_path)).replace("\\", "/")
|
|
|
|
# Pre-process image paths to be absolute for xhtml2pdf
|
|
import re
|
|
def replace_img(match):
|
|
alt = match.group(1)
|
|
src = match.group(2)
|
|
if not os.path.isabs(src) and not src.startswith("http"):
|
|
src = os.path.join(base_dir, src).replace("\\", "/")
|
|
return f''
|
|
|
|
md_text = re.sub(r'!\[(.*?)\]\((.*?)\)', replace_img, md_text)
|
|
|
|
# Convert Markdown to HTML
|
|
html_content = markdown.markdown(
|
|
md_text,
|
|
extensions=['tables', 'fenced_code', 'toc', 'sane_lists', 'admonition']
|
|
)
|
|
|
|
# Footer content
|
|
footer = """
|
|
<div id="footer_content" style="text-align: right; color: #666; font-size: 9pt;">
|
|
iT Guys - Documentação Técnica - <pdf:pagenumber>
|
|
</div>
|
|
"""
|
|
|
|
final_html = f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<style>{CSS_STYLES}</style>
|
|
</head>
|
|
<body>
|
|
{footer}
|
|
<div class="content">
|
|
{html_content}
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
# Generate PDF with xhtml2pdf
|
|
with open(output_path, "w+b") as result_file:
|
|
pisa_status = pisa.CreatePDF(
|
|
final_html,
|
|
dest=result_file,
|
|
encoding='utf-8'
|
|
)
|
|
|
|
if pisa_status.err:
|
|
raise Exception("PDF generation failed due to xhtml2pdf errors.")
|
|
|
|
return True
|