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/
|
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()
|
||||||
|
|||||||
Reference in New Issue
Block a user