diff --git a/web_iconify_proxy/README.rst b/web_iconify_proxy/README.rst
index 738966d83..33658c429 100644
--- a/web_iconify_proxy/README.rst
+++ b/web_iconify_proxy/README.rst
@@ -42,8 +42,34 @@ Usage
=====
This module works in conjunction with web_iconify. Once installed, icons
-will be served through the proxy and cached locally. No specific usage
-instructions are required.
+will be served through the proxy and cached locally.
+
+SVG Icon Parameters
+-------------------
+
+You can customize SVG icons by adding query parameters to the URL. The
+format is:
+
+``/web_iconify_proxy//.svg?param1=value1¶m2=value2...``
+
+Available parameters:
+
+- **color**: Icon color (e.g., ``color=red``, ``color=%23ff0000``).
+- **width**: Icon width (e.g., ``width=50``, ``width=50px``).
+- **height**: Icon height (e.g., ``height=50``, ``height=50px``). If
+ only one dimension is specified, the other will be calculated
+ automatically to maintain aspect ratio.
+- **flip**: Flip the icon. Possible values: ``horizontal``,
+ ``vertical``, or both (e.g., ``flip=horizontal``, ``flip=vertical``,
+ ``flip=horizontal,vertical``).
+- **rotate**: Rotate the icon by 90, 180, or 270 degrees (e.g.,
+ ``rotate=90``, ``rotate=180``).
+- **box**: Set to ``true`` to add an empty rectangle to the SVG that
+ matches the viewBox (e.g., ``box=true``).
+
+Example:
+
+``/web_iconify_proxy/mdi/home.svg?color=blue&width=64&flip=horizontal``
Changelog
=========
diff --git a/web_iconify_proxy/controllers/main.py b/web_iconify_proxy/controllers/main.py
index 696fd01ce..b7c9ad934 100644
--- a/web_iconify_proxy/controllers/main.py
+++ b/web_iconify_proxy/controllers/main.py
@@ -1,3 +1,4 @@
+import ast
import base64
import datetime
import logging
@@ -15,7 +16,13 @@ class IconifyProxyController(http.Controller):
"""Controller for proxying Iconify requests."""
def _fetch_iconify_data(
- self, upstream_url, content_type, prefix, icons=None, icon=None
+ self,
+ upstream_url,
+ content_type,
+ prefix,
+ icons=None,
+ icon=None,
+ normalized_params_string="",
):
"""Fetches data from the Iconify API or the local cache.
@@ -25,22 +32,28 @@ class IconifyProxyController(http.Controller):
prefix (str): The icon prefix.
icons (str, optional): Comma-separated list of icons (for CSS and JSON).
icon (str, optional): The icon name (for SVG).
+ normalized_params_string (str, optional): Normalized parameters string.
Returns:
Response: The HTTP response.
"""
+ # Validate prefix
+ prefix = prefix.lower()
+
# Validate prefix
if not re.match(r"^[a-z0-9-]+$", prefix):
raise request.not_found()
# Validate icon (if provided)
- if icon and not re.match(r"^[a-z0-9:-]+$", icon):
- raise request.not_found()
+ if icon:
+ icon = icon.lower()
+ if not re.match(r"^[a-z0-9:-]+$", icon):
+ raise request.not_found()
# Validate icons (if provided)
if icons:
- icon_list = icons.split(",")
+ icon_list = [i.lower() for i in icons.split(",")]
for single_icon in icon_list:
if not re.match(r"^[a-z0-9:-]+$", single_icon):
raise request.not_found()
@@ -48,13 +61,13 @@ class IconifyProxyController(http.Controller):
Attachment = request.env["ir.attachment"].sudo()
if content_type == "image/svg+xml":
- name = f"{prefix}-{icon}"
+ name = f"{prefix}-{icon}-{normalized_params_string.lower()}"
res_model = "iconify.svg"
elif content_type == "text/css":
- name = f"{prefix}-{icons}"
+ name = f"{prefix}-{icons}-{normalized_params_string.lower()}"
res_model = "iconify.css"
elif content_type == "application/json":
- name = f"{prefix}-{icons}"
+ name = f"{prefix}-{icons}-{normalized_params_string.lower()}"
res_model = "iconify.json"
else:
raise request.not_found()
@@ -98,6 +111,36 @@ class IconifyProxyController(http.Controller):
]
return request.make_response(data, headers)
+ def _normalize_params_common(self, params):
+ """Normalizes common parameters for Iconify requests."""
+ normalized = []
+ for key in sorted(params.keys()): # Sort keys alphabetically
+ normalized.append(f"{key.lower()}={params[key]}")
+ return ";".join(normalized)
+
+ def _normalize_params_svg(self, params):
+ """Normalizes parameters specifically for SVG requests."""
+ allowed_params = ["color", "width", "height", "flip", "rotate", "box"]
+ normalized = []
+ for key in sorted(params.keys()):
+ if key in allowed_params:
+ value = params[key]
+ key = key.lower()
+ # Basic type validation
+ if key in ("width", "height", "rotate") and not (
+ isinstance(value, str) or isinstance(value, int)
+ ):
+ continue # Skip invalid values
+ if key == "box":
+ try:
+ value = ast.literal_eval(str(value).lower())
+ if not isinstance(value, bool):
+ continue
+ except (ValueError, SyntaxError):
+ continue
+ normalized.append(f"{key}={value}")
+ return ";".join(normalized)
+
@http.route(
"/web_iconify_proxy//.svg",
type="http",
@@ -115,9 +158,21 @@ class IconifyProxyController(http.Controller):
Returns:
Response: The HTTP response containing the SVG data.
"""
- upstream_url = f"https://api.iconify.design/{prefix}/{icon}.svg"
+ normalized_params = self._normalize_params_svg(params)
+ if normalized_params:
+ query_string = normalized_params.replace(";", "&")
+ upstream_url = (
+ f"https://api.iconify.design/{prefix}/{icon}.svg?{query_string}"
+ )
+ else:
+ upstream_url = f"https://api.iconify.design/{prefix}/{icon}.svg"
+
return self._fetch_iconify_data(
- upstream_url, "image/svg+xml", prefix, icon=icon
+ upstream_url,
+ "image/svg+xml",
+ prefix,
+ icon=icon,
+ normalized_params_string=normalized_params,
)
@http.route(
diff --git a/web_iconify_proxy/readme/USAGE.md b/web_iconify_proxy/readme/USAGE.md
index 4387a1100..ff4dbaf9d 100644
--- a/web_iconify_proxy/readme/USAGE.md
+++ b/web_iconify_proxy/readme/USAGE.md
@@ -1,3 +1,21 @@
This module works in conjunction with web_iconify. Once installed, icons
-will be served through the proxy and cached locally. No specific usage
-instructions are required.
+will be served through the proxy and cached locally.
+
+## SVG Icon Parameters
+
+You can customize SVG icons by adding query parameters to the URL. The format is:
+
+`/web_iconify_proxy//.svg?param1=value1¶m2=value2...`
+
+Available parameters:
+
+* **color**: Icon color (e.g., `color=red`, `color=%23ff0000`).
+* **width**: Icon width (e.g., `width=50`, `width=50px`).
+* **height**: Icon height (e.g., `height=50`, `height=50px`). If only one dimension is specified, the other will be calculated automatically to maintain aspect ratio.
+* **flip**: Flip the icon. Possible values: `horizontal`, `vertical`, or both (e.g., `flip=horizontal`, `flip=vertical`, `flip=horizontal,vertical`).
+* **rotate**: Rotate the icon by 90, 180, or 270 degrees (e.g., `rotate=90`, `rotate=180`).
+* **box**: Set to `true` to add an empty rectangle to the SVG that matches the viewBox (e.g., `box=true`).
+
+Example:
+
+`/web_iconify_proxy/mdi/home.svg?color=blue&width=64&flip=horizontal`
diff --git a/web_iconify_proxy/static/description/index.html b/web_iconify_proxy/static/description/index.html
index e8dc1c60c..020a34cc4 100644
--- a/web_iconify_proxy/static/description/index.html
+++ b/web_iconify_proxy/static/description/index.html
@@ -377,16 +377,19 @@ ir.attachment model.
This module works in conjunction with web_iconify. Once installed, icons
-will be served through the proxy and cached locally. No specific usage
-instructions are required.
+will be served through the proxy and cached locally.
+
color: Icon color (e.g., color=red, color=%23ff0000).
+
width: Icon width (e.g., width=50, width=50px).
+
height: Icon height (e.g., height=50, height=50px). If
+only one dimension is specified, the other will be calculated
+automatically to maintain aspect ratio.
+
flip: Flip the icon. Possible values: horizontal,
+vertical, or both (e.g., flip=horizontal, flip=vertical,
+flip=horizontal,vertical).
+
rotate: Rotate the icon by 90, 180, or 270 degrees (e.g.,
+rotate=90, rotate=180).
+
box: Set to true to add an empty rectangle to the SVG that
+matches the viewBox (e.g., box=true).
Bugs are tracked on GitHub Issues.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
@@ -415,21 +440,21 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
Do not contact contributors directly about support or help with technical issues.