| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- """
- Path Converter.
- pymdownx.pathconverter
- An extension for Python Markdown.
- An extension to covert tag paths to relative or absolute:
- Given an absolute base and a target relative path, this extension searches for file
- references that are relative and converts them to a path relative
- to the base path.
- -or-
- Given an absolute base path, this extension searches for file
- references that are relative and converts them to absolute paths.
- MIT license.
- Copyright (c) 2014 - 2017 Isaac Muse <isaacmuse@gmail.com>
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions
- of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
- TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
- """
- from markdown import Extension
- from markdown.postprocessors import Postprocessor
- from . import util
- import os
- import re
- from urllib.parse import urlunparse
- RE_TAG_HTML = r'''(?xus)
- (?:
- (?P<comments>(\r?\n?\s*)<!--[\s\S]*?-->(\s*)(?=\r?\n)|<!--[\s\S]*?-->)|
- (?P<open><(?P<tag>(?:%s)))
- (?P<attr>(?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'))?)*)
- (?P<close>\s*(?:\/?)>)
- )
- '''
- RE_TAG_LINK_ATTR = re.compile(
- r'''(?xus)
- (?P<attr>
- (?:
- (?P<name>\s+(?:href|src)\s*=\s*)
- (?P<path>"[^"]*"|'[^']*')
- )
- )
- '''
- )
- def repl_relative(m, base_path, relative_path):
- """Replace path with relative path."""
- link = m.group(0)
- try:
- scheme, netloc, path, params, query, fragment, is_url, is_absolute = util.parse_url(m.group('path')[1:-1])
- if not is_url:
- # Get the absolute path of the file or return
- # if we can't resolve the path
- path = util.url2path(path)
- if (not is_absolute):
- # Convert current relative path to absolute
- path = os.path.relpath(
- os.path.normpath(os.path.join(base_path, path)),
- os.path.normpath(relative_path)
- )
- # Convert the path, URL encode it, and format it as a link
- path = util.path2url(path)
- link = '%s"%s"' % (
- m.group('name'),
- urlunparse((scheme, netloc, path, params, query, fragment))
- )
- except Exception: # pragma: no cover
- # Parsing crashed and burned; no need to continue.
- pass
- return link
- def repl_absolute(m, base_path):
- """Replace path with absolute path."""
- link = m.group(0)
- try:
- scheme, netloc, path, params, query, fragment, is_url, is_absolute = util.parse_url(m.group('path')[1:-1])
- if (not is_absolute and not is_url):
- path = util.url2path(path)
- path = os.path.normpath(os.path.join(base_path, path))
- path = util.path2url(path)
- start = '/' if not path.startswith('/') else ''
- link = '%s"%s%s"' % (
- m.group('name'),
- start,
- urlunparse((scheme, netloc, path, params, query, fragment))
- )
- except Exception: # pragma: no cover
- # Parsing crashed and burned; no need to continue.
- pass
- return link
- def repl(m, base_path, rel_path=None):
- """Replace."""
- if m.group('comments'):
- tag = m.group('comments')
- else:
- tag = m.group('open')
- if rel_path is None:
- tag += RE_TAG_LINK_ATTR.sub(lambda m2: repl_absolute(m2, base_path), m.group('attr'))
- else:
- tag += RE_TAG_LINK_ATTR.sub(lambda m2: repl_relative(m2, base_path, rel_path), m.group('attr'))
- tag += m.group('close')
- return tag
- class PathConverterPostprocessor(Postprocessor):
- """Post process to find tag lings to convert."""
- def run(self, text):
- """Find and convert paths."""
- basepath = self.config['base_path']
- relativepath = self.config['relative_path']
- absolute = bool(self.config['absolute'])
- tags = re.compile(RE_TAG_HTML % '|'.join(self.config['tags'].split()))
- if not absolute and basepath and relativepath:
- text = tags.sub(lambda m: repl(m, basepath, relativepath), text)
- elif absolute and basepath:
- text = tags.sub(lambda m: repl(m, basepath), text)
- return text
- class PathConverterExtension(Extension):
- """PathConverter extension."""
- def __init__(self, *args, **kwargs):
- """Initialize."""
- self.config = {
- 'base_path': ["", "Base path used to find files - Default: \"\""],
- 'relative_path': ["", "Path that files will be relative to (not needed if using absolute) - Default: \"\""],
- 'absolute': [False, "Paths are absolute by default; disable for relative - Default: False"],
- 'tags': ["img script a link", "tags to convert src and/or href in - Default: 'img scripts a link'"]
- }
- super(PathConverterExtension, self).__init__(*args, **kwargs)
- def extendMarkdown(self, md):
- """Add post processor to Markdown instance."""
- rel_path = PathConverterPostprocessor(md)
- rel_path.config = self.getConfigs()
- md.postprocessors.register(rel_path, "path-converter", 2)
- md.registerExtension(self)
- def makeExtension(*args, **kwargs):
- """Return extension."""
- return PathConverterExtension(*args, **kwargs)
|