Table of Contents
As a developer, I often find myself needing to convert Markdown files into visually appealing formats.
This led me to create a Python script that transforms Markdown files into PNG images.
This utility leverages popular Python libraries such as Markdown for rendering text as HTML and Selenium with ChromeDriver for capturing high-quality screenshots of the rendered HTML.
Let me walk you through how this script works and why it's such a handy tool for transforming documentation into visual formats.
The Inspiration Behind the Script
I frequently work with Markdown for documentation and writing articles due to its simplicity and readability.
However, there are times when I need to share this content in a more visually engaging format, such as on social media or in presentations.
This script was born out of the need to automate this conversion process, making it more efficient.
This book offers an in-depth exploration of Python's magic methods, examining the mechanics and applications that make these features essential to Python's design.
The Full Script
First, make sure to have the necessary libraries installed:
pip install markdown selenium webdriver-manager pymdown-extensions
Here's the complete Python script that performs the conversion:
from markdown import markdown
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
import os
import time
def markdown_to_png(md_path, output_path='output.png'):
"""
Convert a markdown file to a PNG image.
Args:
md_path (str): Path to the markdown file
output_path (str): Path where the PNG will be saved
"""
try:
# Convert markdown to HTML
md_text = Path(md_path).read_text()
html_body = markdown(md_text, extensions=['tables', 'fenced_code'])
# HTML template with styling
html_template = """
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
padding: 40px;
max-width: 800px;
margin: auto;
background: white;
line-height: 1.6;
color: #24292e;
}
table {
border-collapse: collapse;
width: 100%;
margin: 16px 0;
font-size: 14px;
}
th, td {
border: 1px solid #e1e4e8;
padding: 8px 16px;
text-align: left;
}
th {
background-color: #f6f8fa;
font-weight: 600;
}
tr:nth-child(even) {
background-color: #fafbfc;
}
tr:hover {
background-color: #f6f8fa;
}
pre {
background: #f6f8fa;
border-radius: 6px;
padding: 16px;
overflow: auto;
font-size: 14px;
line-height: 1.45;
margin: 16px 0;
}
code {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 85%;
}
pre code {
padding: 0;
background: transparent;
}
h1 {
font-size: 2em;
padding-bottom: 0.3em;
border-bottom: 1px solid #eaecef;
}
</style>
</head>
<body>
{content}
</body>
</html>
"""
# Create full HTML
full_html = html_template.replace("{content}", html_body)
# Save to temporary file
temp_html = "temp.html"
with open(temp_html, "w", encoding="utf-8") as f:
f.write(full_html)
# Configure Chrome
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--window-size=1000,800")
chrome_options.add_argument("--hide-scrollbars")
# Initialize Chrome and take screenshot
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
try:
driver.get(f"file://{os.path.abspath(temp_html)}")
time.sleep(1) # Wait for rendering
driver.save_screenshot(output_path)
if os.path.exists(output_path):
print(f"Successfully saved PNG to {output_path}")
else:
print("Failed to create output file")
finally:
driver.quit()
if os.path.exists(temp_html):
os.remove(temp_html)
except Exception as e:
print(f"Error: {str(e)}")
return
if __name__ == "__main__":
markdown_to_png('example.md', 'example_output.png')
Explanation
Reading the Markdown File: The script opens and reads the content of a Markdown file specified by markdown_file
.
Converting Markdown to HTML: The markdown.markdown()
function converts the Markdown content to HTML, with extensions for tables and fenced code blocks to ensure proper formatting.
HTML Template with Styling: The HTML template includes links to external stylesheets (Highlight.js for code highlighting) and custom CSS for styling elements like tables, headers, and code blocks. The {content}
placeholder is replaced with the converted HTML content.
Saving the HTML to a Temporary File: The HTML content is written to a temporary file (temp.html
).
Setting Up Selenium WebDriver: Chrome options are configured to run in headless mode (without a GUI) and set a window size to fit the content. The Service
object manages the ChromeDriver installation.
Loading the HTML and Taking a Screenshot: Selenium loads the temporary HTML file in the browser and takes a screenshot, saving it as a PNG image specified by output_image
.
Cleaning Up: The WebDriver session is closed, and the temporary HTML file is deleted to keep the workspace clean.
Example Usage: The function markdown_to_png
is called with an example Markdown file (example.md
) and output image path (example_output.png
).
Running an Example
Let's see the script in action with this markdown example:
# Sample Markdown Table
This is a basic table to test Markdown-to-PNG conversion.
| Name | Age | Role |
| ------- | --- | ------------ |
| Alice | 30 | Developer |
| Bob | 25 | Designer |
| Charlie | 35 | Product Lead |
You can also add a code block:
```python
def greet(name):
return f"Hello, {name}!"
```
example.md
Which the script converts to:

If you are interested in some of my other scripts, check this article about a mult-function script for resizing images, generate passwords and convert currencies:
Practical Applications
This approach is valuable in scenarios where visual documentation is preferred over plain text.
For example:
- Content Distribution: Sharing well-formatted snippets on social media, blogs, or even as part of a presentation.
- Automated Reporting: Generating visual reports where the data is originally in Markdown format.
- Digital Archiving: Converting documentation into image formats for archiving, ensuring the layout remains consistent across different platforms.
Conclusion
By combining Markdown conversion, HTML styling, and automated browser control, this script is a creative way to bridge the gap between text-based content and visual media.
And for me, it solve a almost daily need of nicely formatted and shareable content.
The use of external libraries like Selenium and ChromeDriver means that developers can quickly integrate this functionality into their workflow, transforming documentation into a more engaging visual format with minimal effort.
If you like this content and you found it useful, consider donating to support the free content on this blog:
Get the full source code at:
My name is Nuno Bispo (a.k.a Developer Service) and I love to teach and share my knowledge. This blog is mostly focused on Python, Django and AI, but Javascript related content also appears from time to time.
Feel free to leave your comment and suggest new content ideas, I am always looking for new things to learn and share.
Follow me on Twitter: https://twitter.com/DevAsService
Follow me on Instagram: https://www.instagram.com/devasservice/
Follow me on TikTok: https://www.tiktok.com/@devasservice
Follow me on YouTube: https://www.youtube.com/@DevAsService