Remove minimal style, add mono style, make elegant default

- Removed minimal style (not useful)
- Added mono style with JetBrains Mono font
- Changed default from default to elegant
- Fixed bullet consistency with list-style-type: disc

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-26 15:01:58 +02:00
parent 48829d4007
commit 630660adb2

275
md2pdf.py
View File

@@ -12,7 +12,7 @@ Usage:
md2pdf.py docs/ -o pdf_output/ # Creates PDFs in pdf_output/ md2pdf.py docs/ -o pdf_output/ # Creates PDFs in pdf_output/
# With custom style # With custom style
md2pdf.py input.md --style minimal # Use minimal style (no colors) md2pdf.py input.md --style mono # Use monospace style
Requires: pip install markdown weasyprint Requires: pip install markdown weasyprint
""" """
@@ -26,6 +26,107 @@ from weasyprint.text.fonts import FontConfiguration
# Style templates # Style templates
STYLES = { STYLES = {
"elegant": """
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap');
@page {
size: A4;
margin: 1.8cm 2cm;
}
body {
font-family: 'Lato', 'Helvetica Neue', Helvetica, sans-serif;
font-weight: 300;
font-size: 9.5pt;
line-height: 1.55;
color: #2c2c2c;
}
h1 {
font-weight: 300;
font-size: 22pt;
color: #1a1a1a;
margin: 0 0 0.3em 0;
letter-spacing: 0.5pt;
}
h2 {
font-weight: 400;
font-size: 11pt;
color: #444;
margin: 1.2em 0 0.4em 0;
padding-bottom: 0.2em;
border-bottom: 1px solid #e0e0e0;
text-transform: uppercase;
letter-spacing: 1pt;
}
h3 {
font-weight: 400;
font-size: 10pt;
color: #333;
margin: 0.9em 0 0.3em 0;
}
h4 {
font-weight: 400;
font-size: 9.5pt;
color: #555;
margin: 0.7em 0 0.2em 0;
}
p {
margin: 0.4em 0;
}
code {
font-family: 'SF Mono', Menlo, monospace;
font-size: 8.5pt;
background: #f8f8f8;
padding: 1px 4px;
border-radius: 2px;
}
pre {
background: #f8f8f8;
padding: 0.8em;
font-size: 8pt;
border-left: 2px solid #ddd;
}
pre code { background: none; padding: 0; }
table {
width: 100%;
border-collapse: collapse;
font-size: 9pt;
margin: 0.8em 0;
}
th, td {
padding: 0.4em;
text-align: left;
border-bottom: 1px solid #eee;
}
th { font-weight: 400; color: #666; }
ul, ol {
margin: 0.3em 0;
padding-left: 0;
list-style-position: inside;
list-style-type: disc;
}
ol { list-style-type: decimal; }
li {
margin: 0.15em 0;
}
blockquote {
margin: 0.8em 0;
padding-left: 1em;
border-left: 2px solid #ccc;
color: #666;
font-style: italic;
}
a {
color: #2c2c2c;
text-decoration: none;
border-bottom: 1px solid #ccc;
}
strong { font-weight: 400; }
em { font-style: italic; }
hr {
border: none;
border-top: 1px solid #e5e5e5;
margin: 1.2em 0;
}
""",
"default": """ "default": """
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600&display=swap');
@page { @page {
@@ -108,7 +209,9 @@ STYLES = {
margin: 0.5em 0; margin: 0.5em 0;
padding-left: 0; padding-left: 0;
list-style-position: inside; list-style-position: inside;
list-style-type: disc;
} }
ol { list-style-type: decimal; }
li { li {
margin: 0.2em 0; margin: 0.2em 0;
} }
@@ -132,49 +235,6 @@ STYLES = {
margin: 1.5em 0; margin: 1.5em 0;
} }
""", """,
"minimal": """
@page {
size: A4;
margin: 2cm;
}
body {
font-family: Georgia, 'Times New Roman', serif;
font-size: 11pt;
line-height: 1.7;
color: #000;
}
h1, h2, h3, h4 {
font-family: -apple-system, BlinkMacSystemFont, Arial, sans-serif;
color: #000;
}
h1 { font-size: 22pt; margin-top: 0; }
h2 { font-size: 16pt; margin-top: 1.5em; }
h3 { font-size: 13pt; margin-top: 1.2em; }
h4 { font-size: 11pt; margin-top: 1em; }
code {
font-family: Menlo, Monaco, monospace;
font-size: 10pt;
background: #f5f5f5;
padding: 1px 4px;
}
pre {
background: #f5f5f5;
padding: 1em;
font-size: 9pt;
border: 1px solid #ddd;
}
pre code { background: none; padding: 0; }
table { border-collapse: collapse; width: 100%; margin: 1em 0; }
th, td { border: 1px solid #000; padding: 0.4em; text-align: left; }
th { font-weight: bold; }
blockquote {
margin: 1em 2em;
font-style: italic;
color: #555;
}
ul, ol { padding-left: 0; list-style-position: inside; }
a { color: #000; }
""",
"dark": """ "dark": """
@page { @page {
size: A4; size: A4;
@@ -223,113 +283,119 @@ STYLES = {
padding: 0.5em 1em; padding: 0.5em 1em;
margin: 1em 0; margin: 1em 0;
} }
ul, ol { padding-left: 0; list-style-position: inside; } ul, ol { padding-left: 0; list-style-position: inside; list-style-type: disc; }
ol { list-style-type: decimal; }
a { color: #818cf8; } a { color: #818cf8; }
hr { border: none; border-top: 1px solid #374151; margin: 2em 0; } hr { border: none; border-top: 1px solid #374151; margin: 2em 0; }
""", """,
"elegant": """ "mono": """
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500&display=swap');
@page { @page {
size: A4; size: A4;
margin: 1.8cm 2cm; margin: 2cm;
} }
body { body {
font-family: 'Lato', 'Helvetica Neue', Helvetica, sans-serif; font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', Consolas, monospace;
font-weight: 300; font-weight: 300;
font-size: 9.5pt; font-size: 9pt;
line-height: 1.55; line-height: 1.6;
color: #2c2c2c; color: #1a1a1a;
}
h1, h2, h3, h4 {
font-weight: 500;
color: #000;
} }
h1 { h1 {
font-weight: 300; font-size: 16pt;
font-size: 22pt; margin-top: 0;
color: #1a1a1a; margin-bottom: 0.8em;
margin: 0 0 0.3em 0; padding-bottom: 0.3em;
letter-spacing: 0.5pt; border-bottom: 2px solid #000;
} }
h2 { h2 {
font-weight: 400; font-size: 12pt;
font-size: 11pt; margin-top: 1.5em;
color: #444; margin-bottom: 0.5em;
margin: 1.2em 0 0.4em 0;
padding-bottom: 0.2em;
border-bottom: 1px solid #e0e0e0;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 1pt; letter-spacing: 1pt;
} }
h3 { h3 {
font-weight: 400;
font-size: 10pt; font-size: 10pt;
color: #333; margin-top: 1.2em;
margin: 0.9em 0 0.3em 0; margin-bottom: 0.4em;
} }
h4 { h4 {
font-weight: 400; font-size: 9pt;
font-size: 9.5pt; margin-top: 1em;
color: #555;
margin: 0.7em 0 0.2em 0;
} }
p { p {
margin: 0.4em 0; margin: 0.5em 0;
} }
code { code {
font-family: 'SF Mono', Menlo, monospace; background-color: #f0f0f0;
font-size: 8.5pt;
background: #f8f8f8;
padding: 1px 4px; padding: 1px 4px;
border-radius: 2px; border-radius: 2px;
} }
pre { pre {
background: #f8f8f8; background-color: #f5f5f5;
padding: 0.8em; padding: 1em;
font-size: 8pt; border: 1px solid #ddd;
border-left: 2px solid #ddd; font-size: 8.5pt;
line-height: 1.5;
}
pre code {
background: none;
padding: 0;
} }
pre code { background: none; padding: 0; }
table { table {
width: 100%;
border-collapse: collapse; border-collapse: collapse;
font-size: 9pt; width: 100%;
margin: 0.8em 0; margin: 1em 0;
font-size: 8.5pt;
} }
th, td { th, td {
border: 1px solid #ccc;
padding: 0.4em; padding: 0.4em;
text-align: left; text-align: left;
border-bottom: 1px solid #eee;
} }
th { font-weight: 400; color: #666; } th {
background: #f0f0f0;
font-weight: 500;
}
ul, ol { ul, ol {
margin: 0.3em 0; margin: 0.5em 0;
padding-left: 0; padding-left: 0;
list-style-position: inside; list-style-position: inside;
list-style-type: disc;
} }
ol { list-style-type: decimal; }
li { li {
margin: 0.15em 0; margin: 0.2em 0;
} }
blockquote { blockquote {
margin: 0.8em 0; border-left: 3px solid #999;
padding-left: 1em; margin: 1em 0;
border-left: 2px solid #ccc; padding: 0.5em 1em;
color: #666; color: #555;
font-style: italic; font-style: italic;
} }
a { a {
color: #2c2c2c; color: #000;
text-decoration: none; text-decoration: underline;
border-bottom: 1px solid #ccc; }
strong {
font-weight: 500;
} }
strong { font-weight: 400; }
em { font-style: italic; }
hr { hr {
border: none; border: none;
border-top: 1px solid #e5e5e5; border-top: 1px solid #ccc;
margin: 1.2em 0; margin: 1.5em 0;
} }
""" """
} }
def convert_md_to_pdf(md_file: Path, output_file: Path, style: str = "default") -> Path: def convert_md_to_pdf(md_file: Path, output_file: Path, style: str = "elegant") -> Path:
"""Convert a single markdown file to PDF""" """Convert a single markdown file to PDF"""
with open(md_file, 'r', encoding='utf-8') as f: with open(md_file, 'r', encoding='utf-8') as f:
@@ -346,7 +412,7 @@ def convert_md_to_pdf(md_file: Path, output_file: Path, style: str = "default")
] ]
) )
css = STYLES.get(style, STYLES["default"]) css = STYLES.get(style, STYLES["elegant"])
full_html = f"""<!DOCTYPE html> full_html = f"""<!DOCTYPE html>
<html> <html>
@@ -372,22 +438,21 @@ def main():
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=""" epilog="""
Examples: Examples:
%(prog)s document.md Convert single file %(prog)s document.md Convert single file (elegant style)
%(prog)s document.md -o report.pdf Convert with custom output name %(prog)s document.md -o report.pdf Convert with custom output name
%(prog)s docs/ Convert all .md files in directory %(prog)s docs/ Convert all .md files in directory
%(prog)s docs/ -o pdf/ Convert directory to different output %(prog)s docs/ -o pdf/ Convert directory to different output
%(prog)s doc.md --style minimal Use minimal style %(prog)s doc.md --style mono Use monospace font
%(prog)s doc.md --style dark Use dark theme %(prog)s doc.md --style dark Use dark theme
%(prog)s doc.md --style elegant Use elegant style (ideal for CVs)
Available styles: default, minimal, dark, elegant Available styles: elegant (default), default, dark, mono
""" """
) )
parser.add_argument('input', help='Input markdown file or directory') parser.add_argument('input', help='Input markdown file or directory')
parser.add_argument('-o', '--output', help='Output PDF file or directory') parser.add_argument('-o', '--output', help='Output PDF file or directory')
parser.add_argument('--style', choices=list(STYLES.keys()), default='default', parser.add_argument('--style', choices=list(STYLES.keys()), default='elegant',
help='Style template (default: default)') help='Style template (default: elegant)')
parser.add_argument('-q', '--quiet', action='store_true', help='Suppress output') parser.add_argument('-q', '--quiet', action='store_true', help='Suppress output')
args = parser.parse_args() args = parser.parse_args()