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:
275
md2pdf.py
275
md2pdf.py
@@ -12,7 +12,7 @@ Usage:
|
||||
md2pdf.py docs/ -o pdf_output/ # Creates PDFs in pdf_output/
|
||||
|
||||
# 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
|
||||
"""
|
||||
@@ -26,6 +26,107 @@ from weasyprint.text.fonts import FontConfiguration
|
||||
|
||||
# Style templates
|
||||
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": """
|
||||
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600&display=swap');
|
||||
@page {
|
||||
@@ -108,7 +209,9 @@ STYLES = {
|
||||
margin: 0.5em 0;
|
||||
padding-left: 0;
|
||||
list-style-position: inside;
|
||||
list-style-type: disc;
|
||||
}
|
||||
ol { list-style-type: decimal; }
|
||||
li {
|
||||
margin: 0.2em 0;
|
||||
}
|
||||
@@ -132,49 +235,6 @@ STYLES = {
|
||||
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": """
|
||||
@page {
|
||||
size: A4;
|
||||
@@ -223,113 +283,119 @@ STYLES = {
|
||||
padding: 0.5em 1em;
|
||||
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; }
|
||||
hr { border: none; border-top: 1px solid #374151; margin: 2em 0; }
|
||||
""",
|
||||
"elegant": """
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap');
|
||||
"mono": """
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500&display=swap');
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 1.8cm 2cm;
|
||||
margin: 2cm;
|
||||
}
|
||||
body {
|
||||
font-family: 'Lato', 'Helvetica Neue', Helvetica, sans-serif;
|
||||
font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', Consolas, monospace;
|
||||
font-weight: 300;
|
||||
font-size: 9.5pt;
|
||||
line-height: 1.55;
|
||||
color: #2c2c2c;
|
||||
font-size: 9pt;
|
||||
line-height: 1.6;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
h1, h2, h3, h4 {
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
}
|
||||
h1 {
|
||||
font-weight: 300;
|
||||
font-size: 22pt;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 0.3em 0;
|
||||
letter-spacing: 0.5pt;
|
||||
font-size: 16pt;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.8em;
|
||||
padding-bottom: 0.3em;
|
||||
border-bottom: 2px solid #000;
|
||||
}
|
||||
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;
|
||||
font-size: 12pt;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.5em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1pt;
|
||||
}
|
||||
h3 {
|
||||
font-weight: 400;
|
||||
font-size: 10pt;
|
||||
color: #333;
|
||||
margin: 0.9em 0 0.3em 0;
|
||||
margin-top: 1.2em;
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
h4 {
|
||||
font-weight: 400;
|
||||
font-size: 9.5pt;
|
||||
color: #555;
|
||||
margin: 0.7em 0 0.2em 0;
|
||||
font-size: 9pt;
|
||||
margin-top: 1em;
|
||||
}
|
||||
p {
|
||||
margin: 0.4em 0;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
code {
|
||||
font-family: 'SF Mono', Menlo, monospace;
|
||||
font-size: 8.5pt;
|
||||
background: #f8f8f8;
|
||||
background-color: #f0f0f0;
|
||||
padding: 1px 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
pre {
|
||||
background: #f8f8f8;
|
||||
padding: 0.8em;
|
||||
font-size: 8pt;
|
||||
border-left: 2px solid #ddd;
|
||||
background-color: #f5f5f5;
|
||||
padding: 1em;
|
||||
border: 1px solid #ddd;
|
||||
font-size: 8.5pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
pre code { background: none; padding: 0; }
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 9pt;
|
||||
margin: 0.8em 0;
|
||||
width: 100%;
|
||||
margin: 1em 0;
|
||||
font-size: 8.5pt;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.4em;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
th { font-weight: 400; color: #666; }
|
||||
th {
|
||||
background: #f0f0f0;
|
||||
font-weight: 500;
|
||||
}
|
||||
ul, ol {
|
||||
margin: 0.3em 0;
|
||||
margin: 0.5em 0;
|
||||
padding-left: 0;
|
||||
list-style-position: inside;
|
||||
list-style-type: disc;
|
||||
}
|
||||
ol { list-style-type: decimal; }
|
||||
li {
|
||||
margin: 0.15em 0;
|
||||
margin: 0.2em 0;
|
||||
}
|
||||
blockquote {
|
||||
margin: 0.8em 0;
|
||||
padding-left: 1em;
|
||||
border-left: 2px solid #ccc;
|
||||
color: #666;
|
||||
border-left: 3px solid #999;
|
||||
margin: 1em 0;
|
||||
padding: 0.5em 1em;
|
||||
color: #555;
|
||||
font-style: italic;
|
||||
}
|
||||
a {
|
||||
color: #2c2c2c;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid #ccc;
|
||||
color: #000;
|
||||
text-decoration: underline;
|
||||
}
|
||||
strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
strong { font-weight: 400; }
|
||||
em { font-style: italic; }
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
margin: 1.2em 0;
|
||||
border-top: 1px solid #ccc;
|
||||
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"""
|
||||
|
||||
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>
|
||||
<html>
|
||||
@@ -372,22 +438,21 @@ def main():
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
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 docs/ Convert all .md files in directory
|
||||
%(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 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('-o', '--output', help='Output PDF file or directory')
|
||||
parser.add_argument('--style', choices=list(STYLES.keys()), default='default',
|
||||
help='Style template (default: default)')
|
||||
parser.add_argument('--style', choices=list(STYLES.keys()), default='elegant',
|
||||
help='Style template (default: elegant)')
|
||||
parser.add_argument('-q', '--quiet', action='store_true', help='Suppress output')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
Reference in New Issue
Block a user