added support for Obsidian style image references, including embedded dimensions

This commit is contained in:
Amy G. Bowersox 2024-03-13 22:02:01 -06:00
parent c0025f9513
commit 5e144d303d

View File

@ -1,5 +1,6 @@
#/usr/bin/env python3
import re
import markdown
import xml.etree.ElementTree as etree
from urllib.parse import urlparse
@ -60,6 +61,98 @@ class MetaStripper(Extension):
md.preprocessors.register(MetaStripper.MetaStripperProc(md), 'metastripper', PRIO_BASE)
class ObsidianImages(Extension):
"""An extension that supports image tags the way Obsidian handles them."""
DIMS = re.compile(r'(.*)\|(\d+)(?:x(\d+))?')
def __init__(self, context, **kwargs):
super(ObsidianImages, self).__init__(**kwargs)
self._context = context
@property
def invalid_reference_classname(self):
return 'invalid-reference'
def _parse_dimensions(self, s):
m = self.DIMS.match(s)
if m:
width = int(m.group(2))
height = int(m.group(3)) if m.group(3) else -1
return m.group(1), width, height
else:
return s, -1, -1
def _lookup_image_reference(self, name):
node, _ = self._context.src_index.lookup(name)
if node:
return node.link_target(self._context.url_prefix)
return None
class ObsidianImageProc(InlineProcessor):
def __init__(self, pattern, md, extref):
super(ObsidianImages.ObsidianImageProc, self).__init__(pattern, md)
self._extref = extref
def handleMatch(self, m, data):
name, width, height = self._extref._parse_dimensions(m.group(1))
link = self._extref._lookup_image_reference(name)
if link is None:
el = etree.Element('span')
el.set('class', self._extref.invalid_reference_classname)
el.text = name
else:
el = etree.Element('img')
el.set('src', link)
el.set('alt', name)
if width > 0:
el.set('width', str(width))
if height > 0:
el.set('height', str(height))
return el, m.start(0), m.end(0)
class GenericImageProc(InlineProcessor):
def __init__(self, pattern, md, extref):
super(ObsidianImages.GenericImageProc, self).__init__(pattern, md)
self._extref = extref
def handleMatch(self, m, data):
name, width, height = self._extref._parse_dimensions(m.group(1))
link = m.group(2)
if is_proper_url(link):
el = etree.Element('img')
el.set('src', link)
if len(name) > 0:
el.set('alt', name)
if width > 0:
el.set('width', str(width))
if height > 0:
el.set('height', str(height))
else:
newlink = self._extref._lookup_image_reference(link)
if newlink:
el = etree.Element('img')
el.set('src', newlink)
if len(name) > 0:
el.set('alt', name)
if width > 0:
el.set('width', str(width))
if height > 0:
el.set('height', str(height))
else:
el = etree.Element('span')
el.set('class', self._extref.invalid_reference_classname)
el.text = link
return el, m.start(0), m.end(0)
def extendMarkdown(self, md):
OBSIMAGE_PATTERN = r'!\[\[(.*?)\]\]'
GENERICIMAGE_PATTERN = r'!\[(.*?)\]\((.*?)\)'
md.inlinePatterns.register(ObsidianImages.ObsidianImageProc(OBSIMAGE_PATTERN, md, self),
'obsidian_images', PRIO_BASE + 1010)
md.inlinePatterns.register(ObsidianImages.GenericImageProc(GENERICIMAGE_PATTERN, md, self),
'obsidian_generic_images', PRIO_BASE + 1000)
class ObsidianLinks(Extension):
"""
An extension that processes Obsidian internal links in the [[page name]] format, as well as overrides the standard
@ -151,4 +244,4 @@ class ObsidianInlines(Extension):
def create_markdown_parser(context):
return markdown.Markdown(extensions=[MetaStripper(), ObsidianLinks(context), ObsidianInlines()])
return markdown.Markdown(extensions=[MetaStripper(), ObsidianImages(context), ObsidianLinks(context), ObsidianInlines()])