338 lines
9.5 KiB
Python
338 lines
9.5 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# pylint:disable=invalid-name,unused-argument,too-many-arguments
|
|
|
|
"""
|
|
Use fontawesome icons
|
|
"""
|
|
|
|
import re
|
|
import uuid
|
|
|
|
from docutils import nodes
|
|
from docutils.parsers.rst import Directive
|
|
from docutils.parsers.rst import directives
|
|
|
|
from sphinx.writers.html import HTMLTranslator
|
|
from sphinx.writers.latex import LaTeXTranslator
|
|
from sphinx.writers.texinfo import TexinfoTranslator
|
|
from sphinx.writers.text import TextTranslator
|
|
from sphinx.writers.manpage import ManualPageTranslator
|
|
from sphinx.util.osutil import relative_uri
|
|
|
|
__version_info__ = (2, 0, 0)
|
|
__version__ = '.'.join([str(val) for val in __version_info__])
|
|
|
|
|
|
class fa(nodes.General, nodes.Inline, nodes.Element):
|
|
"""Generic node for FontAwesome"""
|
|
|
|
|
|
class falink(nodes.General, nodes.Inline, nodes.Element):
|
|
"""Generic link node for FontAwesome"""
|
|
|
|
|
|
def append_fa_image(self: HTMLTranslator, node: fa or falink) -> None:
|
|
"""Add image to node"""
|
|
path = {
|
|
'brands': self.builder.config.fa_brands_path,
|
|
'regular': self.builder.config.fa_regular_path,
|
|
'solid': self.builder.config.fa_solid_path,
|
|
}[node['iconset']]
|
|
|
|
path = relative_uri(
|
|
self.builder.current_docname,
|
|
self.builder.get_asset_paths()[0] + '/' + path
|
|
)
|
|
|
|
label_uid = uuid.uuid4()
|
|
title = None
|
|
options = 'role="img"'
|
|
options += ' xmlns="http://www.w3.org/2000/svg"'
|
|
options += ' xmlns:xlink="http://www.w3.org/1999/xlink"'
|
|
if node.get('alt', None):
|
|
options += f' aria-labelledby="fa_{label_uid}"'
|
|
title = f'<title id="{label_uid}">{node["alt"]}</title>'
|
|
else:
|
|
options += ' aria-hidden="true" xlink:title=""'
|
|
|
|
if node.get('html_id', None):
|
|
options += f' id={node["html_id"]}'
|
|
|
|
options += f' class="fasvg {node.get("html_class", "") or ""}"'
|
|
|
|
self.body.append(
|
|
f'<svg {options}>'
|
|
)
|
|
|
|
if title:
|
|
self.body.append(title)
|
|
|
|
self.body.append(
|
|
f'<use xlink:href="{path}#{node["icon"]}"></use></svg>'
|
|
)
|
|
|
|
|
|
def html_visit_fa(self: HTMLTranslator, node: fa) -> None:
|
|
"""Rendering FA node in HTML"""
|
|
append_fa_image(self, node)
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def latex_visit_fa(self: LaTeXTranslator, node: fa) -> None:
|
|
"""Rendering FA node in LaTeX"""
|
|
if 'alt' in node.attributes:
|
|
self.body.append(f'[{node["alt"]}]')
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def texinfo_visit_fa(self: TexinfoTranslator, node: fa) -> None:
|
|
"""Rendering FA node in TeXinfo"""
|
|
if 'alt' in node.attributes:
|
|
self.body.append(f'[{node["alt"]}]')
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def text_visit_fa(self: TextTranslator, node: fa) -> None:
|
|
"""Rendering FA node in text"""
|
|
if 'alt' in node.attributes:
|
|
self.add_text(f'[{node["alt"]}]')
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def gemini_visit_fa(self, node: fa) -> None:
|
|
"""Rendering FA node in Gemini"""
|
|
if 'alt' in node.attributes:
|
|
self.add_text(f'[{node["alt"]}]')
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def man_visit_fa(self: ManualPageTranslator, node: fa) -> None:
|
|
"""Rendering FA node in Man file"""
|
|
if 'alt' in node.attributes:
|
|
self.body.append(f'[{node["alt"]}]')
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def create_fa_node(iconset, icon, html_id=None, html_class=None, alt=None):
|
|
"""Create FA node"""
|
|
node = fa()
|
|
node['iconset'] = iconset
|
|
node['icon'] = icon
|
|
node['html_id'] = html_id
|
|
node['html_class'] = html_class
|
|
node['alt'] = alt or ''
|
|
return node
|
|
|
|
|
|
def html_visit_falink(self: HTMLTranslator, node: fa) -> None:
|
|
"""Rendering FA link node in HTML"""
|
|
self.body.append(
|
|
f'<a class="fasvglink {node["icon"]}" href="{node["url"]}">'
|
|
)
|
|
append_fa_image(self, node)
|
|
|
|
self.body.append(f' {node["text"]}</a>')
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def latex_visit_falink(self: LaTeXTranslator, node: fa) -> None:
|
|
"""Rendering FA link node in LaTeX"""
|
|
self.body.append(
|
|
f'\\href{{{node["url"]}}}'
|
|
f'{{{node["alt"]} {node["text"]}}}'
|
|
)
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def texinfo_visit_falink(self: TexinfoTranslator, node: fa) -> None:
|
|
"""Rendering FA link node in TexInfo"""
|
|
self.body.append(
|
|
f'\\href{{{node["url"]}}}{{{node["alt"]} {node["text"]}}}'
|
|
)
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def text_visit_falink(self: TextTranslator, node: fa) -> None:
|
|
"""Rendering FA link node in text"""
|
|
self.add_text(
|
|
f'{node["alt"]} {node["text"]} <{node["url"]}>'
|
|
)
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def gemini_visit_falink(self, node: fa) -> None:
|
|
"""Rendering FA link node in Gemini"""
|
|
self.end_block()
|
|
self.add_text(
|
|
f'=> {node["alt"]} {node["url"]} {node["text"]}'
|
|
)
|
|
self.end_block()
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def man_visit_falink(self: ManualPageTranslator, node: fa) -> None:
|
|
"""Rendering FA link node in Man file"""
|
|
self.body.append(f'{node["text"]} {node["alt"]} <{node["url"]}>')
|
|
raise nodes.SkipNode
|
|
|
|
|
|
def create_falink_node(iconset, text):
|
|
"""Create a new link node depending of text and iconset"""
|
|
node = falink()
|
|
regex = re.compile(
|
|
r'(?P<icon> *[a-zA-Z-_]* *):(?P<text>.*)'
|
|
+ r'(?P<alt>\[.*\] *)?<(?P<url>.*)>')
|
|
parsed = regex.search(text)
|
|
node['iconset'] = iconset
|
|
node['icon'] = parsed.group('icon').strip()
|
|
node['url'] = parsed.group('url').strip()
|
|
node['alt'] = (parsed.group('alt') or '').strip().strip('[]')
|
|
node['text'] = parsed.group('text').strip()
|
|
return node
|
|
|
|
|
|
def fab(role, rawtext, text, lineno, inliner, options=None, content=None):
|
|
"""Node for FontAwesome brand icon"""
|
|
if not options:
|
|
options = {}
|
|
if not content:
|
|
content = []
|
|
regex = re.compile(r'(?P<icon>[a-zA-Z-_]*)(?P<alt>\[.*\] *)?')
|
|
parsed = regex.search(text)
|
|
alt = (parsed.group('alt') or '').strip().strip('[]')
|
|
icon = parsed.group('icon').strip()
|
|
return [create_fa_node('brands', icon, alt=alt)], []
|
|
|
|
|
|
def far(role, rawtext, text, lineno, inliner, options=None, content=None):
|
|
"""Node for FontAwesome regular icon"""
|
|
if not options:
|
|
options = {}
|
|
if not content:
|
|
content = []
|
|
regex = re.compile(r'(?P<icon>[a-zA-Z-_]*)(?P<alt>\[.*\] *)?')
|
|
parsed = regex.search(text)
|
|
alt = (parsed.group('alt') or '').strip().strip('[]')
|
|
icon = parsed.group('icon').strip()
|
|
return [create_fa_node('regular', icon, alt=alt)], []
|
|
|
|
|
|
def fas(role, rawtext, text, lineno, inliner, options=None, content=None):
|
|
"""Node for FontAwesome solid icon"""
|
|
if not options:
|
|
options = {}
|
|
if not content:
|
|
content = []
|
|
regex = re.compile(r'(?P<icon>[a-zA-Z-_]*)(?P<alt>\[.*\] *)?')
|
|
parsed = regex.search(text)
|
|
alt = (parsed.group('alt') or '').strip().strip('[]')
|
|
icon = parsed.group('icon').strip()
|
|
return [create_fa_node('solid', icon, alt=alt)], []
|
|
|
|
|
|
def fablink(role, rawtext, text, lineno, inliner, options=None, content=None):
|
|
"""Node for link with FontAwesome brands iconset"""
|
|
if not options:
|
|
options = {}
|
|
if not content:
|
|
content = []
|
|
return [create_falink_node('brands', text)], []
|
|
|
|
|
|
def farlink(role, rawtext, text, lineno, inliner, options=None, content=None):
|
|
"""Node for link with FontAwesome regular iconset"""
|
|
if not options:
|
|
options = {}
|
|
if not content:
|
|
content = []
|
|
return [create_falink_node('regular', text)], []
|
|
|
|
|
|
def faslink(role, rawtext, text, lineno, inliner, options=None, content=None):
|
|
"""Node for link with FontAwesome solid iconset"""
|
|
if not options:
|
|
options = {}
|
|
if not content:
|
|
content = []
|
|
return [create_falink_node('solid', text)], []
|
|
|
|
|
|
class FaDirective(Directive):
|
|
""" Main directive for FontAwesome icons """
|
|
|
|
has_content = False
|
|
required_arguments = 1
|
|
final_argument_whitespace = True
|
|
option_spec = {
|
|
"class": directives.unchanged,
|
|
"id": directives.unchanged,
|
|
"alt": directives.unchanged,
|
|
}
|
|
iconset = None
|
|
|
|
def run(self):
|
|
node = create_fa_node(
|
|
self.iconset,
|
|
self.arguments[0],
|
|
self.options['id'],
|
|
self.options['class'],
|
|
self.options['alt']
|
|
)
|
|
return [node]
|
|
|
|
|
|
class Fab(FaDirective):
|
|
""" Directive for FontAwesome brands iconset """
|
|
iconset = 'brands'
|
|
|
|
|
|
class Far(FaDirective):
|
|
""" Directive for FontAwesome regular iconset """
|
|
iconset = 'regular'
|
|
|
|
|
|
class Fas(FaDirective):
|
|
""" Directive for FontAwesome solid iconset """
|
|
iconset = 'solid'
|
|
|
|
|
|
def setup(app):
|
|
"""
|
|
Setup Sphinx app
|
|
"""
|
|
app.add_node(
|
|
fa,
|
|
html=(html_visit_fa, None),
|
|
epub=(html_visit_fa, None),
|
|
latex=(latex_visit_fa, None),
|
|
texinfo=(texinfo_visit_fa, None),
|
|
text=(text_visit_fa, None),
|
|
man=(man_visit_fa, None),
|
|
gemini=(gemini_visit_fa, None),
|
|
)
|
|
app.add_node(
|
|
falink,
|
|
html=(html_visit_falink, None),
|
|
epub=(html_visit_falink, None),
|
|
latex=(latex_visit_falink, None),
|
|
texinfo=(texinfo_visit_falink, None),
|
|
text=(text_visit_falink, None),
|
|
man=(man_visit_falink, None),
|
|
gemini=(gemini_visit_falink, None),
|
|
)
|
|
app.add_config_value('fa_brands_path', 'fa/brands.svg', True)
|
|
app.add_config_value('fa_regular_path', 'fa/regular.svg', True)
|
|
app.add_config_value('fa_solid_path', 'fa/solid.svg', True)
|
|
app.add_role('fab', fab)
|
|
app.add_role('fas', fas)
|
|
app.add_role('far', far)
|
|
app.add_role('fablink', fablink)
|
|
app.add_role('faslink', faslink)
|
|
app.add_role('farlink', farlink)
|
|
app.add_directive('fab', Fab)
|
|
app.add_directive('fas', Fas)
|
|
app.add_directive('far', Far)
|
|
return {'version': __version__}
|