ActiveState Community

Markdown to HTML page [need help]

Posted by florentv on 2009-04-10 14:55
OS: OS X

Hello,

As a self-imposed exercise, i'm trying to build a small Python macro that will let me create a copy of the current file, filtered through Markdown.

After a few hours working on this (i'm a front-end developer, and know very little Python), this is what i was able to write:

import os.path
import string
import komodo
import markdown2

# Let's make a template with placeholders
page = """
<!doctype html>
<html lang="
${lang}">
<head>
        <meta charset="
${charset}">
        <title>${title}</title>
</head>
<body>
${content}
</body>
</html>
"
""

# Getting some paths and filenames for the current file
source_file_path = komodo.interpolate('%F')
source_file_dir = komodo.interpolate('%D')
source_file_name = komodo.interpolate('%f')
source_file_noext = komodo.interpolate('%b')

# Get content
source_file = open(source_file_path, 'r')
source_file_content = source_file.read()
source_file.close()

# Apply Markdown filter and fill in the template
new_file_content = markdown2.markdown(source_file_content)
new_file_html = string.Template(page).substitute(
    lang='fr',
    charset='utf-8',
    title='Test',
    content=new_file_content
)

# Finally, write to file
new_file_path = os.path.join(source_file_dir,source_file_noext+'.test')
new_file = open(new_file_path, 'w')
new_file.write(new_file_html)
new_file.close()

import os.path
import string
import komodo
import markdown2

# Let's make a template with placeholders
page = """
<!doctype html>
<html lang="${lang}">
<head>
	<meta charset="${charset}">
	<title>${title}</title>
</head>
<body>
${content}
</body>
</html>
"""

# Getting some paths and filenames for the current file
source_file_path = komodo.interpolate('%F')
source_file_dir = komodo.interpolate('%D')
source_file_name = komodo.interpolate('%f')
source_file_noext = komodo.interpolate('%b')

# Get content
source_file = open(source_file_path, 'r')
source_file_content = source_file.read()
source_file.close()

# Apply Markdown filter and fill in the template
new_file_content = markdown2.markdown(source_file_content)
new_file_html = string.Template(page).substitute(
    lang='fr',
    charset='utf-8',
    title='Test',
    content=new_file_content
)

# Finally, write to file
new_file_path = os.path.join(source_file_dir,source_file_noext+'.test')
new_file = open(new_file_path, 'w')
new_file.write(new_file_html)
new_file.close()

This code kinda works as a macro. You just have to get Komodo to use the markdown2 module, which i did by putting it in Komodo's own Python installation (ugly, i suppose).

So right now with this code there are two big issues, and maybe some potential issues you may want to point at.

First issue is the fact i had to put the markdown2.py in Komodo's Python installation by hand. Surely there is a better way to enable this module for a macro?

Second issue is that every line in the template gets padded with 4 spaces. Which means i have 4 spaces before the doctype, or the <html> tag, well before anything but the second and following lines from the source file. Any idea about what could be provoking this?

Finally, there may be some obvious mistakes or malpractice in this code, feel free to point them out. I know very little Python, or even programming, so i'm bound not to do the right thing.

toddw | Sat, 2009-04-11 10:10

For the first one, I usually prefer to hack the sys.path to append the required Python module directory.

For the 4-space padding problem, this is a limitation of the Python macro system (each line gets indented 4 spaces). You can work around it by using different string formatting technique (a little ugly though):

page = '\n' +
'<!doctype html>\n' +
'<html lang="${lang}">\n' +
'<head>\n' +
'       <meta charset="${charset}>\n' +
'       <title>${title}</title>\n' +
'</head>\n' +
'<body>\n' +
'${content}\n' +
'</body>\n' +
'</html>\n'
page = '\n' +
'<!doctype html>\n' +
'<html lang="${lang}">\n' +
'<head>\n' +
'	<meta charset="${charset}>\n' +
'	<title>${title}</title>\n' +
'</head>\n' +
'<body>\n' +
'${content}\n' +
'</body>\n' +
'</html>\n'

Cheers,
Todd

florentv | Sat, 2009-04-11 11:56

This code yields a Syntax Error. The only solution i found was this:

page =  '<!doctype html>\n'
page += '<html lang="${lang}">\n'
page += '<head>\n'
page += '       <meta charset="${charset}">\n'
page += '       <title>${title}</title>\n'
page += '</head>\n'
page += '<body>\n'
page += '${content}\n'
page += '</body>\n'
page += '</html>\n'
page =  '<!doctype html>\n'
page += '<html lang="${lang}">\n'
page += '<head>\n'
page += '	<meta charset="${charset}">\n'
page += '	<title>${title}</title>\n'
page += '</head>\n'
page += '<body>\n'
page += '${content}\n'
page += '</body>\n'
page += '</html>\n'

…or using an external file to store the template.

I wonder if you can get the contents of a toolbox snippet from a macro, though.

justquick | Thu, 2009-05-07 00:17

Very nice job, I have just a few things to add. I changed some syntax and re-templated the page w/o the string module (deprecated). I also added a bit that opens the page in your favorite web browser when its done. I would like to see how to make the output appear in the embedded browser in komodo... maybe someday. Here is my src:

import os.path
import komodo
import markdown2
import webbrowser

# Let's make a template with placeholders
page = """
<!doctype html>
<html lang="
%(lang)">
<head>
        <meta charset="
%(charset)">
        <title>%(title)</title>
</head>
<body>
%(content)
</body>
</html>
"
""

# Getting some paths and filenames for the current file
source_file_path = komodo.interpolate('%F')
source_file_dir = komodo.interpolate('%D')
source_file_name = komodo.interpolate('%f')
source_file_noext = komodo.interpolate('%b')

# Get content
source_file_content = open(source_file_path, 'r').read()

# Apply Markdown filter and fill in the template
new_file_html = page % dict(
    lang='fr',
    charset='utf-8',
    title='Test',
    content=markdown2.markdown(source_file_content)
)

# Finally, write to file
new_file_path = os.path.join(source_file_dir,source_file_noext+'.html')
new_file = open(new_file_path, 'w')
new_file.write(new_file_html)
new_file.close()

webbrowser.open(new_file_path)

import os.path
import komodo
import markdown2
import webbrowser

# Let's make a template with placeholders
page = """
<!doctype html>
<html lang="%(lang)">
<head>
        <meta charset="%(charset)">
        <title>%(title)</title>
</head>
<body>
%(content)
</body>
</html>
"""

# Getting some paths and filenames for the current file
source_file_path = komodo.interpolate('%F')
source_file_dir = komodo.interpolate('%D')
source_file_name = komodo.interpolate('%f')
source_file_noext = komodo.interpolate('%b')

# Get content
source_file_content = open(source_file_path, 'r').read()

# Apply Markdown filter and fill in the template 
new_file_html = page % dict(
    lang='fr',
    charset='utf-8',
    title='Test',
    content=markdown2.markdown(source_file_content)
)

# Finally, write to file
new_file_path = os.path.join(source_file_dir,source_file_noext+'.html')
new_file = open(new_file_path, 'w')
new_file.write(new_file_html)
new_file.close()

webbrowser.open(new_file_path)

florentv | Thu, 2009-05-07 01:41

What i use now is a bit different, the main addition is that i parse the generated HTML to find the content of the first H1 to use it as a page title (title tag in HTML).

I took the template mechanism (didn't know that the string module was deprecated, i found it while searching for a way to create a string template in Python), but i would get errors with it when i didn't specify a conversion flag.

So instead of "%(sometext)", i had to write "%(sometext)s", or the letter just after the closing parenthesis would be picked up as a conversion flag, and then an error would be raised. Not sure why this happens, the conversion flag is supposed to be optional by the Python doc. :/

Here's what i have:

import sys
import os
import komodo

sys.path.append('/Users/florent/Outils/Libs/python')
import markdown2

# Let's make a template with placeholders
page =  """
<!doctype html>
<html lang="
%(lang)s">
<head>
<meta charset="
%(charset)s">
<title>%(title)s</title>
<link rel="
stylesheet" href="%(style)s" />
</head>
<body>
%(content)s
</body>
</html>
"
""

# Getting some paths and filenames for the current file
source_file_path = komodo.interpolate('%F')
source_file_dir = komodo.interpolate('%D')
source_file_name = komodo.interpolate('%f')
source_file_noext = komodo.interpolate('%b')

# Get content, apply Markdown filter
source_file_content = open(source_file_path, 'r').read()
new_file_content = markdown2.markdown(source_file_content)

# Trying to find a title for the page
pos1, pos2 = new_file_content.find('<h1>'), new_file_content.find('</h1>')
if 0 < pos2 - pos1 < 80:
    new_file_title = new_file_content[pos1 + 4:pos2]
else:
    new_file_title = ''

# Fill in the template
new_file_html = page % dict(
    lang='fr',
    charset='utf-8',
    title=new_file_title,
    style='http://covertprestige.info/public/css/all.css',
    content=new_file_content
)

# Finally, write to file
new_file_path = os.path.join(source_file_dir,source_file_name+'.html')
new_file = open(new_file_path, 'w')
new_file.write(new_file_html)
new_file.close()

import sys
import os
import komodo

sys.path.append('/Users/florent/Outils/Libs/python')
import markdown2

# Let's make a template with placeholders
page =  """
<!doctype html>
<html lang="%(lang)s">
<head>
<meta charset="%(charset)s">
<title>%(title)s</title>
<link rel="stylesheet" href="%(style)s" />
</head>
<body>
%(content)s
</body>
</html>
"""

# Getting some paths and filenames for the current file
source_file_path = komodo.interpolate('%F')
source_file_dir = komodo.interpolate('%D')
source_file_name = komodo.interpolate('%f')
source_file_noext = komodo.interpolate('%b')

# Get content, apply Markdown filter
source_file_content = open(source_file_path, 'r').read()
new_file_content = markdown2.markdown(source_file_content)

# Trying to find a title for the page
pos1, pos2 = new_file_content.find('<h1>'), new_file_content.find('</h1>')
if 0 < pos2 - pos1 < 80:
    new_file_title = new_file_content[pos1 + 4:pos2]
else:
    new_file_title = ''

# Fill in the template
new_file_html = page % dict(
    lang='fr',
    charset='utf-8',
    title=new_file_title,
    style='http://covertprestige.info/public/css/all.css',
    content=new_file_content
)

# Finally, write to file
new_file_path = os.path.join(source_file_dir,source_file_name+'.html')
new_file = open(new_file_path, 'w')
new_file.write(new_file_html)
new_file.close()

stan | Thu, 2009-05-07 05:38

I've had an open issue http://code.google.com/p/komodo-html-toolkit/issues/detail?id=9 to add this to HTML Toolkit. Your code gets the job done, mind if I include it in the toolkit at some point ?

florentv | Thu, 2009-05-07 13:24

mind if I include it in the toolkit at some point ?

Not one bit. But i didn't know you could mix JavaScript and Python in extensions. Also, there's the issue that the user needs to have Markdown somewhere. Oh, and it seems there's a JavaScript implementation of Markdown here: http://attacklab.net/showdown/