Skip to content

Commit

Permalink
added support for Pandoc-style image attributes (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpoore committed Apr 19, 2021
1 parent 4f98a6d commit 944852c
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

## v0.6.0 (dev)

* Added support for Pandoc-style attributes on images:
`![alt_text](image_file){#id .class1 .class2 width=10em height=5em}` (#41).
* Executable code blocks now use PATH to locate executables under Windows, and
thus now work with Python environments. Previously PATH was ignored under
Windows due to the implementation details of Python's `subprocess.Popen()`
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,14 @@ operating systems. All image paths not starting with `http://` or `https://`
are assumed to refer to local image files (files on your machine), and will
result in errors if these files are not found.

[Pandoc-style attributes](https://pandoc.org/MANUAL.html#images) can be used
with images:
```
![alt_text](image_file){#id .class1 .class2 width=10em height=5em}
```
This allows image id, classes, and dimensions to be specified without
resorting to HTML.


### LaTeX

Expand Down
14 changes: 9 additions & 5 deletions text2qti/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
import markdown.extensions.footnotes
import markdown.extensions.tables
import markdown.extensions.md_in_html
from markdown.inlinepatterns import ImageInlineProcessor, IMAGE_LINK_RE

from .config import Config
from .err import Text2qtiError
from .version import __version__ as version
from . import pymd_pandoc_attr


md_extensions = [
markdown.extensions.smarty.makeExtension(),
markdown.extensions.sane_lists.makeExtension(),
Expand All @@ -40,12 +48,8 @@
markdown.extensions.footnotes.makeExtension(),
markdown.extensions.tables.makeExtension(),
markdown.extensions.md_in_html.makeExtension(),
pymd_pandoc_attr.makeExtension(),
]
from markdown.inlinepatterns import ImageInlineProcessor, IMAGE_LINK_RE

from .config import Config
from .err import Text2qtiError
from .version import __version__ as version



Expand Down
105 changes: 105 additions & 0 deletions text2qti/pymd_pandoc_attr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021, Geoffrey M. Poore
# All rights reserved.
#
# Licensed under the BSD 3-Clause License:
# http://opensource.org/licenses/BSD-3-Clause
#


'''
Pandoc-style attribute syntax for Python-Markdown.
Attribute support is currently limited to images.
See https://pandoc.org/MANUAL.html#images.
Inspired by https://github.com/Python-Markdown/markdown/blob/master/markdown/extensions/attr_list.py.
'''


from markdown.extensions import Extension
from markdown.treeprocessors import Treeprocessor
from typing import List, Tuple
import re


IDENTIFIER_PATTERN = r'[A-Za-z][0-9A-Za-z_\-]+'
ID_PATTERN = rf'#{IDENTIFIER_PATTERN}'
CLASS_PATTERN = rf'\.{IDENTIFIER_PATTERN}'
KV_PATTERN = rf'{IDENTIFIER_PATTERN}=[0-9A-Za-z_\-%]+'
ATTR_PATTERN = (
r'\{[ ]*(?!\})('
rf'(?:{ID_PATTERN}(?=[ \}}]))?'
rf'(?:{CLASS_PATTERN}(?=[ \}}])|{KV_PATTERN}(?=[ \}}])|[ ]+(?=[^ \}}]))*'
r')[ ]*\}'
)

def _handle_id(scanner: re.Scanner, token: str) -> Tuple[str, str]:
return '#', token[1:]

def _handle_class(scanner: re.Scanner, token: str) -> Tuple[str, str]:
return '.', token[1:]

def _handle_key_value(scanner: re.Scanner, token: str) -> Tuple[str, str]:
return token.split('=', 1)

_scanner = re.Scanner([
(ID_PATTERN, _handle_id),
(CLASS_PATTERN, _handle_class),
(KV_PATTERN, _handle_key_value),
(r'[ ]+', None),
])

def get_attrs(string: str) -> List[Tuple[str, str]]:
'''
Parse a string of attributes `<attrs>` that has already been extracted
from a string of the form `{<attrs>}`. Return a list of attribute tuples
of the form `(<key>, <value>)`.
'''
results, remainder = _scanner.scan(string)
return results


class PandocAttrTreeprocessor(Treeprocessor):
ATTR_RE = re.compile(ATTR_PATTERN)
def run(self, doc):
for elem in doc.iter():
if self.md.is_block_level(elem.tag):
pass
else:
# inline, only for images currently
if elem.tag == 'img' and elem.tail and elem.tail.startswith('{'):
match = self.ATTR_RE.match(elem.tail)
if match:
self.assign_attrs(elem, match.group(1))
elem.tail = elem.tail[match.end():]

def assign_attrs(self, elem, attrs):
'''
Assign attrs to element.
'''
for k, v in get_attrs(attrs):
if k == '#':
elem.set('id', v)
elif k == '.':
elem_class = elem.get('class')
if elem_class:
elem.set('class', f'{elem_class} {v}')
else:
elem.set('class', v)
else:
elem_style = elem.get('style')
if elem_style:
elem.set('style', f'{elem_style} {k}:{v};')
else:
elem.set('style', f'{k}:{v};')


class PandocAttrExtension(Extension):
def extendMarkdown(self, md):
md.treeprocessors.register(PandocAttrTreeprocessor(md), 'pandoc_attr', 8)
md.registerExtension(self)


def makeExtension(**kwargs):
return PandocAttrExtension(**kwargs)
2 changes: 1 addition & 1 deletion text2qti/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-

from .fmtversion import get_version_plus_info
__version__, __version_info__ = get_version_plus_info(0, 6, 0, 'dev', 3)
__version__, __version_info__ = get_version_plus_info(0, 6, 0, 'dev', 4)

0 comments on commit 944852c

Please sign in to comment.