| 1 |
#!/usr/bin/env Python |
| 2 |
""" |
| 3 |
Tables Extension for Python-Markdown |
| 4 |
==================================== |
| 5 |
|
| 6 |
Added parsing of tables to Python-Markdown. |
| 7 |
|
| 8 |
A simple example: |
| 9 |
|
| 10 |
First Header | Second Header |
| 11 |
------------- | ------------- |
| 12 |
Content Cell | Content Cell |
| 13 |
Content Cell | Content Cell |
| 14 |
|
| 15 |
Copyright 2009 - [Waylan Limberg](http://achinghead.com) |
| 16 |
""" |
| 17 |
import markdown |
| 18 |
from markdown.util import etree |
| 19 |
|
| 20 |
|
| 21 |
class TableProcessor(markdown.blockprocessors.BlockProcessor): |
| 22 |
""" Process Tables. """ |
| 23 |
|
| 24 |
def test(self, parent, block): |
| 25 |
rows = block.split('\n') |
| 26 |
return (len(rows) > 2 and '|' in rows[0] and |
| 27 |
'|' in rows[1] and '-' in rows[1] and |
| 28 |
rows[1][0] in ['|', ':', '-']) |
| 29 |
|
| 30 |
def run(self, parent, blocks): |
| 31 |
""" Parse a table block and build table. """ |
| 32 |
block = blocks.pop(0).split('\n') |
| 33 |
header = block[:2] |
| 34 |
rows = block[2:] |
| 35 |
# Get format type (bordered by pipes or not) |
| 36 |
border = False |
| 37 |
if header[0].startswith('|'): |
| 38 |
border = True |
| 39 |
# Get alignment of columns |
| 40 |
align = [] |
| 41 |
for c in self._split_row(header[1], border): |
| 42 |
if c.startswith(':') and c.endswith(':'): |
| 43 |
align.append('center') |
| 44 |
elif c.startswith(':'): |
| 45 |
align.append('left') |
| 46 |
elif c.endswith(':'): |
| 47 |
align.append('right') |
| 48 |
else: |
| 49 |
align.append(None) |
| 50 |
# Build table |
| 51 |
table = etree.SubElement(parent, 'table') |
| 52 |
thead = etree.SubElement(table, 'thead') |
| 53 |
self._build_row(header[0], thead, align, border) |
| 54 |
tbody = etree.SubElement(table, 'tbody') |
| 55 |
for row in rows: |
| 56 |
self._build_row(row, tbody, align, border) |
| 57 |
|
| 58 |
def _build_row(self, row, parent, align, border): |
| 59 |
""" Given a row of text, build table cells. """ |
| 60 |
tr = etree.SubElement(parent, 'tr') |
| 61 |
tag = 'td' |
| 62 |
if parent.tag == 'thead': |
| 63 |
tag = 'th' |
| 64 |
cells = self._split_row(row, border) |
| 65 |
# We use align here rather than cells to ensure every row |
| 66 |
# contains the same number of columns. |
| 67 |
for i, a in enumerate(align): |
| 68 |
c = etree.SubElement(tr, tag) |
| 69 |
try: |
| 70 |
c.text = cells[i].strip() |
| 71 |
except IndexError: |
| 72 |
c.text = "" |
| 73 |
if a: |
| 74 |
c.set('align', a) |
| 75 |
|
| 76 |
def _split_row(self, row, border): |
| 77 |
""" split a row of text into list of cells. """ |
| 78 |
if border: |
| 79 |
if row.startswith('|'): |
| 80 |
row = row[1:] |
| 81 |
if row.endswith('|'): |
| 82 |
row = row[:-1] |
| 83 |
return row.split('|') |
| 84 |
|
| 85 |
|
| 86 |
class TableExtension(markdown.Extension): |
| 87 |
""" Add tables to Markdown. """ |
| 88 |
|
| 89 |
def extendMarkdown(self, md, md_globals): |
| 90 |
""" Add an instance of TableProcessor to BlockParser. """ |
| 91 |
md.parser.blockprocessors.add('table', |
| 92 |
TableProcessor(md.parser), |
| 93 |
'<hashheader') |
| 94 |
|
| 95 |
|
| 96 |
def makeExtension(configs={}): |
| 97 |
return TableExtension(configs=configs) |