diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6fb2bd17..aee3233a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -20,7 +20,7 @@ repos:
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
- rev: v2.37.3
+ rev: v2.38.2
hooks:
- id: pyupgrade
args: [--py37-plus]
@@ -31,7 +31,7 @@ repos:
- id: isort
- repo: https://github.com/psf/black
- rev: 22.6.0
+ rev: 22.8.0
hooks:
- id: black
@@ -52,7 +52,7 @@ repos:
additional_dependencies:
- sphinx~=5.0
- markdown-it-py>=1.0.0,<3.0.0
- - mdit-py-plugins~=0.3.0
+ - mdit-py-plugins~=0.3.1
files: >
(?x)^(
myst_parser/.*py|
diff --git a/docs/conf.py b/docs/conf.py
index e2d0c5dc..199a32a4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -90,6 +90,7 @@
"strikethrough",
"substitution",
"tasklist",
+ "attrs_image",
]
myst_number_code_blocks = ["typescript"]
myst_heading_anchors = 2
diff --git a/docs/syntax/optional.md b/docs/syntax/optional.md
index 4045eb2e..c5ee44ed 100644
--- a/docs/syntax/optional.md
+++ b/docs/syntax/optional.md
@@ -786,6 +786,42 @@ HTML image can also be used inline!
I'm an inline image:
+### Inline attributes
+
+:::{warning}
+This extension is currently experimental, and may change in future versions.
+:::
+
+By adding `"attrs_image"` to `myst_enable_extensions` (in the sphinx `conf.py` [configuration file](https://www.sphinx-doc.org/en/master/usage/configuration.html)),
+you can enable parsing of inline attributes for images.
+
+For example, the following Markdown:
+
+```md
+![image attrs](img/fun-fish.png){#imgattr .bg-primary width="100px" align=center}
+
+{ref}`a reference to the image `
+```
+
+will be parsed as:
+
+![image attrs](img/fun-fish.png){#imgattr .bg-primary width="100px" align=center}
+
+{ref}`a reference to the image `
+
+Inside the curly braces, the following syntax is possible:
+
+- `.foo` specifies `foo` as a class.
+ Multiple classes may be given in this way; they will be combined.
+- `#foo` specifies `foo` as an identifier.
+ An element may have only one identifier;
+ if multiple identifiers are given, the last one is used.
+- `key="value"` or `key=value` specifies a key-value attribute.
+ Quotes are not needed when the value consists entirely of
+ ASCII alphanumeric characters or `_` or `:` or `-`.
+ Backslash escapes may be used inside quoted values.
+- `%` begins a comment, which ends with the next `%` or the end of the attribute (`}`).
+
(syntax/figures)=
## Markdown Figures
diff --git a/myst_parser/config/main.py b/myst_parser/config/main.py
index c1f1c7f1..a134ea7d 100644
--- a/myst_parser/config/main.py
+++ b/myst_parser/config/main.py
@@ -31,6 +31,7 @@ def check_extensions(_, __, value):
diff = set(value).difference(
[
"amsmath",
+ "attrs_image",
"colon_fence",
"deflist",
"dollarmath",
diff --git a/myst_parser/mdit_to_docutils/base.py b/myst_parser/mdit_to_docutils/base.py
index 1709f1bc..cedd6c35 100644
--- a/myst_parser/mdit_to_docutils/base.py
+++ b/myst_parser/mdit_to_docutils/base.py
@@ -785,6 +785,51 @@ def render_image(self, token: SyntaxTreeNode) -> None:
title = token.attrGet("title")
if title:
img_node["title"] = token.attrGet("title")
+
+ # apply other attributes that can be set on the image
+ if "class" in token.attrs:
+ img_node["classes"].extend(str(token.attrs["class"]).split())
+ if "width" in token.attrs:
+ try:
+ width = directives.length_or_percentage_or_unitless(
+ str(token.attrs["width"])
+ )
+ except ValueError:
+ self.create_warning(
+ f"Invalid width value for image: {token.attrs['width']!r}",
+ line=token_line(token, default=0),
+ subtype="image",
+ append_to=self.current_node,
+ )
+ else:
+ img_node["width"] = width
+ if "height" in token.attrs:
+ try:
+ height = directives.length_or_unitless(str(token.attrs["height"]))
+ except ValueError:
+ self.create_warning(
+ f"Invalid height value for image: {token.attrs['height']!r}",
+ line=token_line(token, default=0),
+ subtype="image",
+ append_to=self.current_node,
+ )
+ else:
+ img_node["height"] = height
+ if "align" in token.attrs:
+ if token.attrs["align"] not in ("left", "center", "right"):
+ self.create_warning(
+ f"Invalid align value for image: {token.attrs['align']!r}",
+ line=token_line(token, default=0),
+ subtype="image",
+ append_to=self.current_node,
+ )
+ else:
+ img_node["align"] = token.attrs["align"]
+ if "id" in token.attrs:
+ name = nodes.fully_normalize_name(str(token.attrs["id"]))
+ img_node["names"].append(name)
+ self.document.note_explicit_target(img_node, img_node)
+
self.current_node.append(img_node)
# ### render methods for plugin tokens
diff --git a/myst_parser/parsers/mdit.py b/myst_parser/parsers/mdit.py
index 249acd68..84764957 100644
--- a/myst_parser/parsers/mdit.py
+++ b/myst_parser/parsers/mdit.py
@@ -9,6 +9,7 @@
from markdown_it.renderer import RendererProtocol
from mdit_py_plugins.amsmath import amsmath_plugin
from mdit_py_plugins.anchors import anchors_plugin
+from mdit_py_plugins.attrs import attrs_plugin
from mdit_py_plugins.colon_fence import colon_fence_plugin
from mdit_py_plugins.deflist import deflist_plugin
from mdit_py_plugins.dollarmath import dollarmath_plugin
@@ -100,6 +101,8 @@ def create_md_parser(
md.use(tasklists_plugin)
if "substitution" in config.enable_extensions:
md.use(substitution_plugin, *config.sub_delimiters)
+ if "attrs_image" in config.enable_extensions:
+ md.use(attrs_plugin, after=("image",))
if config.heading_anchors is not None:
md.use(
anchors_plugin,
diff --git a/pyproject.toml b/pyproject.toml
index d375a604..880a4fd4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -37,7 +37,7 @@ dependencies = [
"docutils>=0.15,<0.20",
"jinja2", # required for substitutions, but let sphinx choose version
"markdown-it-py>=1.0.0,<3.0.0",
- "mdit-py-plugins~=0.3.0",
+ "mdit-py-plugins~=0.3.1",
"pyyaml",
"sphinx>=4,<6",
"typing-extensions",
@@ -68,6 +68,7 @@ testing = [
"pytest-regressions",
"pytest-param-files~=0.3.4",
"sphinx-pytest",
+ "sphinx<5.2", # TODO 5.2 changes the attributes of desc/desc_signature nodes
]
[project.scripts]
diff --git a/tests/test_renderers/fixtures/myst-config.txt b/tests/test_renderers/fixtures/myst-config.txt
index 020623c3..668895a2 100644
--- a/tests/test_renderers/fixtures/myst-config.txt
+++ b/tests/test_renderers/fixtures/myst-config.txt
@@ -49,6 +49,8 @@ www.example.com
strike
+
+:1: (WARNING/2) Strikethrough is currently only supported in HTML output [myst.strikethrough]
.
[gfm-disallowed-html] --myst-gfm-only="yes"
@@ -141,3 +143,34 @@ www.commonmark.org/he
+
+
+.
+
+[attrs_image_warnings] --myst-enable-extensions=attrs_image
+.
+![a](b){width=1x height=2x align=other }
+.
+
+
+
+
+ Invalid width value for image: '1x' [myst.image]
+
+
+ Invalid height value for image: '2x' [myst.image]
+
+
+ Invalid align value for image: 'other' [myst.image]
+
+
+:1: (WARNING/2) Invalid width value for image: '1x' [myst.image]
+:1: (WARNING/2) Invalid height value for image: '2x' [myst.image]
+:1: (WARNING/2) Invalid align value for image: 'other' [myst.image]
+.
diff --git a/tests/test_renderers/test_myst_config.py b/tests/test_renderers/test_myst_config.py
index 0f58cd76..31e2444e 100644
--- a/tests/test_renderers/test_myst_config.py
+++ b/tests/test_renderers/test_myst_config.py
@@ -31,4 +31,8 @@ def test_cmdline(file_params):
parser=Parser(),
settings_overrides=settings,
)
- file_params.assert_expected(doctree.pformat(), rstrip_lines=True)
+ output = doctree.pformat()
+ warnings = report_stream.getvalue()
+ if warnings:
+ output += "\n" + warnings
+ file_params.assert_expected(output, rstrip_lines=True)