Add existing to tracked
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
"""
|
||||
Utils dealing with URLs.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from urllib.parse import urljoin, urlparse
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
from . import VERSION
|
||||
|
||||
HTTP_HEADERS = {'User-Agent': f'CairoSVG {VERSION}'}
|
||||
|
||||
URL = re.compile(r'url\((.+)\)')
|
||||
|
||||
|
||||
def normalize_url(url):
|
||||
"""Normalize ``url`` for underlying NT/Unix operating systems.
|
||||
|
||||
The input ``url`` may look like the following:
|
||||
|
||||
- C:\\Directory\\zzz.svg
|
||||
- file://C:\\Directory\\zzz.svg
|
||||
- zzz.svg
|
||||
|
||||
The output ``url`` on NT systems would look like below:
|
||||
|
||||
- file:///C:/Directory/zzz.svg
|
||||
|
||||
"""
|
||||
if url and os.name == 'nt' and not url.startswith('data:'):
|
||||
# Match input ``url`` like the following:
|
||||
# - C:\\Directory\\zzz.svg
|
||||
# - Blah.svg
|
||||
if not url.startswith('file:') and os.path.isabs(url):
|
||||
url = os.path.abspath(url)
|
||||
if '#' in url:
|
||||
url, part = url.rsplit('#', 1)
|
||||
else:
|
||||
part = None
|
||||
url = Path(url).resolve().as_uri()
|
||||
if part is not None:
|
||||
url = url + '#' + part
|
||||
|
||||
# Match input ``url`` like the following:
|
||||
# - file://C:\\Directory\\zzz.svg
|
||||
elif re.match(
|
||||
'^file://[a-z]:', url,
|
||||
re.IGNORECASE | re.MULTILINE | re.DOTALL):
|
||||
url = url.replace('//', '///')
|
||||
url = url.replace('\\', '/')
|
||||
|
||||
return url
|
||||
|
||||
|
||||
def nt_compatible_path(path):
|
||||
"""Provide compatible NT file paths for ``os.path`` functions
|
||||
|
||||
``os.path`` expects NT paths with no ``/`` at the beginning. For
|
||||
example, ``/C:/Directory/zzz.svg`` would fail ``os.path.isfile()``,
|
||||
``os.path.isdir()`` etc. where the expected input for `os.path`
|
||||
functions is ``/C:/Directory/zzz.svg``.
|
||||
|
||||
Currently ``nt_compatible_path`` performs some basic checks and
|
||||
eliminates the unwanted ``/`` at the beginning.
|
||||
|
||||
"""
|
||||
if os.name == 'nt' and re.match(
|
||||
'^/[a-z]:/', path, re.IGNORECASE | re.MULTILINE | re.DOTALL):
|
||||
return re.sub('^/', '', path, re.IGNORECASE | re.MULTILINE | re.DOTALL)
|
||||
else:
|
||||
return path
|
||||
|
||||
|
||||
def fetch(url, resource_type):
|
||||
"""Fetch the content of ``url``.
|
||||
|
||||
``resource_type`` is the mimetype of the resource (currently one of
|
||||
image/*, image/svg+xml, text/css).
|
||||
|
||||
"""
|
||||
return urlopen(Request(url, headers=HTTP_HEADERS)).read()
|
||||
|
||||
|
||||
def safe_fetch(url, resource_type):
|
||||
"""Fetch the content of ``url`` only if it’s a data-URL.
|
||||
|
||||
Otherwise, return an empty SVG.
|
||||
|
||||
"""
|
||||
if url and url.startswith('data:'):
|
||||
return fetch(url, resource_type)
|
||||
return b'<svg width="1" height="1"></svg>'
|
||||
|
||||
|
||||
def parse_url(url, base=None):
|
||||
"""Parse an URL.
|
||||
|
||||
The URL can be surrounded by a ``url()`` string. If ``base`` is not `None`,
|
||||
the "folder" part of it is prepended to the URL.
|
||||
|
||||
"""
|
||||
if url:
|
||||
match = URL.search(url)
|
||||
if match:
|
||||
url = match.group(1)
|
||||
if base:
|
||||
parsed_base = urlparse(base)
|
||||
parsed_url = urlparse(url)
|
||||
if parsed_base.scheme in ('', 'file'):
|
||||
if parsed_url.scheme in ('', 'file'):
|
||||
parsed_base_path = nt_compatible_path(parsed_base.path)
|
||||
parsed_url_path = nt_compatible_path(parsed_url.path)
|
||||
# We are sure that `url` and `base` are both file-like URLs
|
||||
if os.path.isfile(parsed_base_path):
|
||||
if parsed_url_path:
|
||||
# Take the "folder" part of `base`, as
|
||||
# `os.path.join` doesn't strip the file name
|
||||
url = os.path.join(
|
||||
os.path.dirname(parsed_base_path),
|
||||
parsed_url_path)
|
||||
else:
|
||||
url = parsed_base_path
|
||||
elif os.path.isdir(parsed_base_path):
|
||||
if parsed_url_path:
|
||||
url = os.path.join(
|
||||
parsed_base_path, parsed_url_path)
|
||||
else:
|
||||
url = ''
|
||||
else:
|
||||
url = ''
|
||||
if parsed_url.fragment:
|
||||
url = f'{url}#{parsed_url.fragment}'
|
||||
elif parsed_url.scheme in ('', parsed_base.scheme):
|
||||
# `urljoin` automatically uses the "folder" part of `base`
|
||||
url = urljoin(base, url)
|
||||
url = normalize_url(url.strip('\'"'))
|
||||
return urlparse(url or '')
|
||||
|
||||
|
||||
def read_url(url, url_fetcher, resource_type):
|
||||
"""Get bytes in a parsed ``url`` using ``url_fetcher``.
|
||||
|
||||
If ``url_fetcher`` is None a default (no limitations) URLFetcher is used.
|
||||
"""
|
||||
if url.scheme:
|
||||
url = url.geturl()
|
||||
else:
|
||||
url = f'file://{os.path.abspath(url.geturl())}'
|
||||
url = normalize_url(url)
|
||||
|
||||
return url_fetcher(url, resource_type)
|
||||
Reference in New Issue
Block a user