From 59ed5b114a312170b02122182a3ecb69adb035c9 Mon Sep 17 00:00:00 2001
From: Baudouin Raoult
Date: Sat, 27 Jun 2026 16:27:56 +0100
Subject: [PATCH 1/3] chore: add documentation logos
---
logos/README.md | 25 ++++++++++
logos/docs-logo-template.svg | 17 +++++++
logos/make_logo.py | 96 ++++++++++++++++++++++++++++++++++++
3 files changed, 138 insertions(+)
create mode 100644 logos/docs-logo-template.svg
create mode 100755 logos/make_logo.py
diff --git a/logos/README.md b/logos/README.md
index e175627..390b24b 100644
--- a/logos/README.md
+++ b/logos/README.md
@@ -16,6 +16,31 @@ This directory contains the Anemoi logo in three variants:
+## Documentation logos (Read the Docs)
+
+To produce a logo with a custom sub-title under the "anemoi" wordmark — for
+example one per documentation site on Read the Docs — use `make_logo.py`. It
+reads [`docs-logo-template.svg`](docs-logo-template.svg), substitutes your
+string, and centres it automatically (glyph widths are embedded in the script,
+so it has no dependencies and needs no network or font files).
+
+Create the SVG:
+
+```bash
+python3 make_logo.py metadata # writes metadata.svg
+```
+
+Then rasterise it to a 640-pixel-wide PNG (see
+[Creating PNGs from the SVGs](#creating-pngs-from-the-svgs) for installing a
+converter):
+
+```bash
+rsvg-convert -w 640 metadata.svg -o metadata.png
+```
+
+Pass `-o` to choose a different SVG filename, e.g.
+`python3 make_logo.py "inference" -o logo-inference.svg`.
+
## About SVG
The logos are provided as **SVG** (Scalable Vector Graphics). SVG is a vector
diff --git a/logos/docs-logo-template.svg b/logos/docs-logo-template.svg
new file mode 100644
index 0000000..ca973b4
--- /dev/null
+++ b/logos/docs-logo-template.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/logos/make_logo.py b/logos/make_logo.py
new file mode 100755
index 0000000..eefe929
--- /dev/null
+++ b/logos/make_logo.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+"""Render a logo SVG from docs-logo-template.svg with a custom sub-title string.
+
+Reads ``docs-logo-template.svg``, replaces the ``template`` text with the given
+string, and horizontally centres it using the glyph-width table embedded below.
+
+The centre line is taken from the "anemoi" text element in the template, so the
+sub-title always sits centred under the logo word — even if the template moves.
+
+Usage:
+ ./make_logo.py metadata # -> metadata.svg
+ ./make_logo.py "inference" -o foo.svg
+"""
+import argparse
+import re
+import sys
+
+TEMPLATE = "docs-logo-template.svg"
+
+# Advance widths in em units (fraction of font size), keyed by codepoint.
+
+_WIDTHS = {
+ 32: 0.296, 33: 0.444, 34: 0.52, 35: 0.592, 36: 0.592, 37: 0.907,
+ 38: 0.759, 39: 0.296, 40: 0.314, 41: 0.314, 42: 0.482, 43: 0.666,
+ 44: 0.296, 45: 0.296, 46: 0.296, 47: 0.407, 48: 0.592, 49: 0.592,
+ 50: 0.592, 51: 0.592, 52: 0.592, 53: 0.592, 54: 0.592, 55: 0.592,
+ 56: 0.592, 57: 0.592, 58: 0.296, 59: 0.296, 60: 0.666, 61: 0.666,
+ 62: 0.666, 63: 0.556, 64: 0.8, 65: 0.741, 66: 0.648, 67: 0.685,
+ 68: 0.759, 69: 0.63, 70: 0.593, 71: 0.778, 72: 0.759, 73: 0.296,
+ 74: 0.5, 75: 0.722, 76: 0.537, 77: 0.944, 78: 0.815, 79: 0.832,
+ 80: 0.63, 81: 0.852, 82: 0.648, 83: 0.574, 84: 0.574, 85: 0.741,
+ 86: 0.704, 87: 1.0, 88: 0.722, 89: 0.648, 90: 0.63, 91: 0.296,
+ 92: 0.407, 93: 0.296, 94: 0.666, 95: 0.5, 96: 0.26, 97: 0.537,
+ 98: 0.63, 99: 0.482, 100: 0.63, 101: 0.574, 102: 0.37, 103: 0.63,
+ 104: 0.574, 105: 0.26, 106: 0.26, 107: 0.556, 108: 0.26, 109: 0.87,
+ 110: 0.574, 111: 0.61, 112: 0.63, 113: 0.63, 114: 0.407, 115: 0.463,
+ 116: 0.407, 117: 0.574, 118: 0.556, 119: 0.833, 120: 0.556, 121: 0.556,
+ 122: 0.5, 123: 0.37, 124: 0.222, 125: 0.37, 126: 0.666,
+}
+
+
+def text_width(string, size):
+ missing = [c for c in string if ord(c) not in _WIDTHS]
+ if missing:
+ sys.exit(f"error: no width for {missing!r}; only printable ASCII is supported")
+ return sum(_WIDTHS[ord(c)] for c in string) * size
+
+
+def attr(tag, name):
+ m = re.search(rf'{name}="([^"]*)"', tag)
+ if not m:
+ sys.exit(f"error: attribute {name!r} not found in: {tag}")
+ return m.group(1)
+
+
+def find_text(svg, ident):
+ """Return the full content element."""
+ m = re.search(rf']*>.*?', svg)
+ if not m:
+ sys.exit(f'error: no in template')
+ return m.group(0)
+
+
+def main(argv=None):
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+ p.add_argument("text", help="sub-title string to render")
+ p.add_argument("-o", "--output", help="output SVG (default: .svg)")
+ p.add_argument("--template", default=TEMPLATE)
+ args = p.parse_args(argv)
+
+ svg = open(args.template, encoding="utf-8").read()
+
+ # Centre line = centre of the "anemoi" logo word.
+ anemoi = find_text(svg, "anemoi")
+ a_x = float(attr(anemoi, "x"))
+ a_size = float(attr(anemoi, "font-size"))
+ a_content = re.search(r">([^<]*)", anemoi).group(1)
+ centre = a_x + text_width(a_content, a_size) / 2
+
+ # Replace the template sub-title, re-centred on `centre`.
+ tmpl = find_text(svg, "template")
+ t_size = float(attr(tmpl, "font-size"))
+ new_x = centre - text_width(args.text, t_size) / 2
+
+ new_tag = re.sub(r'x="[^"]*"', f'x="{new_x:.4f}"', tmpl, count=1)
+ new_tag = re.sub(r">([^<]*)", f">{args.text}", new_tag, count=1)
+
+ out_svg = svg.replace(tmpl, new_tag, 1)
+ out_path = args.output or f"{args.text}.svg"
+ with open(out_path, "w", encoding="utf-8") as fh:
+ fh.write(out_svg)
+ print(f"wrote {out_path}: {args.text!r} centred on x={centre:.3f} (x={new_x:.4f})", file=sys.stderr)
+
+
+if __name__ == "__main__":
+ main()
From daddf96d9f1261e0396b3d1e8db8a4ff25840eb1 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sat, 27 Jun 2026 15:33:04 +0000
Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
logos/make_logo.py | 121 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 105 insertions(+), 16 deletions(-)
diff --git a/logos/make_logo.py b/logos/make_logo.py
index eefe929..276ead5 100755
--- a/logos/make_logo.py
+++ b/logos/make_logo.py
@@ -1,4 +1,13 @@
#!/usr/bin/env python3
+# (C) Copyright 2026 Anemoi contributors.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
"""Render a logo SVG from docs-logo-template.svg with a custom sub-title string.
Reads ``docs-logo-template.svg``, replaces the ``template`` text with the given
@@ -11,6 +20,7 @@
./make_logo.py metadata # -> metadata.svg
./make_logo.py "inference" -o foo.svg
"""
+
import argparse
import re
import sys
@@ -20,22 +30,101 @@
# Advance widths in em units (fraction of font size), keyed by codepoint.
_WIDTHS = {
- 32: 0.296, 33: 0.444, 34: 0.52, 35: 0.592, 36: 0.592, 37: 0.907,
- 38: 0.759, 39: 0.296, 40: 0.314, 41: 0.314, 42: 0.482, 43: 0.666,
- 44: 0.296, 45: 0.296, 46: 0.296, 47: 0.407, 48: 0.592, 49: 0.592,
- 50: 0.592, 51: 0.592, 52: 0.592, 53: 0.592, 54: 0.592, 55: 0.592,
- 56: 0.592, 57: 0.592, 58: 0.296, 59: 0.296, 60: 0.666, 61: 0.666,
- 62: 0.666, 63: 0.556, 64: 0.8, 65: 0.741, 66: 0.648, 67: 0.685,
- 68: 0.759, 69: 0.63, 70: 0.593, 71: 0.778, 72: 0.759, 73: 0.296,
- 74: 0.5, 75: 0.722, 76: 0.537, 77: 0.944, 78: 0.815, 79: 0.832,
- 80: 0.63, 81: 0.852, 82: 0.648, 83: 0.574, 84: 0.574, 85: 0.741,
- 86: 0.704, 87: 1.0, 88: 0.722, 89: 0.648, 90: 0.63, 91: 0.296,
- 92: 0.407, 93: 0.296, 94: 0.666, 95: 0.5, 96: 0.26, 97: 0.537,
- 98: 0.63, 99: 0.482, 100: 0.63, 101: 0.574, 102: 0.37, 103: 0.63,
- 104: 0.574, 105: 0.26, 106: 0.26, 107: 0.556, 108: 0.26, 109: 0.87,
- 110: 0.574, 111: 0.61, 112: 0.63, 113: 0.63, 114: 0.407, 115: 0.463,
- 116: 0.407, 117: 0.574, 118: 0.556, 119: 0.833, 120: 0.556, 121: 0.556,
- 122: 0.5, 123: 0.37, 124: 0.222, 125: 0.37, 126: 0.666,
+ 32: 0.296,
+ 33: 0.444,
+ 34: 0.52,
+ 35: 0.592,
+ 36: 0.592,
+ 37: 0.907,
+ 38: 0.759,
+ 39: 0.296,
+ 40: 0.314,
+ 41: 0.314,
+ 42: 0.482,
+ 43: 0.666,
+ 44: 0.296,
+ 45: 0.296,
+ 46: 0.296,
+ 47: 0.407,
+ 48: 0.592,
+ 49: 0.592,
+ 50: 0.592,
+ 51: 0.592,
+ 52: 0.592,
+ 53: 0.592,
+ 54: 0.592,
+ 55: 0.592,
+ 56: 0.592,
+ 57: 0.592,
+ 58: 0.296,
+ 59: 0.296,
+ 60: 0.666,
+ 61: 0.666,
+ 62: 0.666,
+ 63: 0.556,
+ 64: 0.8,
+ 65: 0.741,
+ 66: 0.648,
+ 67: 0.685,
+ 68: 0.759,
+ 69: 0.63,
+ 70: 0.593,
+ 71: 0.778,
+ 72: 0.759,
+ 73: 0.296,
+ 74: 0.5,
+ 75: 0.722,
+ 76: 0.537,
+ 77: 0.944,
+ 78: 0.815,
+ 79: 0.832,
+ 80: 0.63,
+ 81: 0.852,
+ 82: 0.648,
+ 83: 0.574,
+ 84: 0.574,
+ 85: 0.741,
+ 86: 0.704,
+ 87: 1.0,
+ 88: 0.722,
+ 89: 0.648,
+ 90: 0.63,
+ 91: 0.296,
+ 92: 0.407,
+ 93: 0.296,
+ 94: 0.666,
+ 95: 0.5,
+ 96: 0.26,
+ 97: 0.537,
+ 98: 0.63,
+ 99: 0.482,
+ 100: 0.63,
+ 101: 0.574,
+ 102: 0.37,
+ 103: 0.63,
+ 104: 0.574,
+ 105: 0.26,
+ 106: 0.26,
+ 107: 0.556,
+ 108: 0.26,
+ 109: 0.87,
+ 110: 0.574,
+ 111: 0.61,
+ 112: 0.63,
+ 113: 0.63,
+ 114: 0.407,
+ 115: 0.463,
+ 116: 0.407,
+ 117: 0.574,
+ 118: 0.556,
+ 119: 0.833,
+ 120: 0.556,
+ 121: 0.556,
+ 122: 0.5,
+ 123: 0.37,
+ 124: 0.222,
+ 125: 0.37,
+ 126: 0.666,
}
From ab81c3ccfeee548d670aa94a1f95ca710db9d015 Mon Sep 17 00:00:00 2001
From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com>
Date: Wed, 15 Jul 2026 16:04:09 +0000
Subject: [PATCH 3/3] feat: add --white flag for dark mode subtitle text
Agent-Logs-Url: https://github.com/ecmwf/anemoi/sessions/418a0dad-5b63-41f9-bded-0eeec3f9c55d
Co-authored-by: HCookie <48088699+HCookie@users.noreply.github.com>
---
logos/make_logo.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/logos/make_logo.py b/logos/make_logo.py
index 276ead5..182dad4 100755
--- a/logos/make_logo.py
+++ b/logos/make_logo.py
@@ -154,6 +154,7 @@ def main(argv=None):
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("text", help="sub-title string to render")
p.add_argument("-o", "--output", help="output SVG (default: .svg)")
+ p.add_argument("--white", action="store_true", help="render sub-title text in white (for dark mode)")
p.add_argument("--template", default=TEMPLATE)
args = p.parse_args(argv)
@@ -173,6 +174,8 @@ def main(argv=None):
new_tag = re.sub(r'x="[^"]*"', f'x="{new_x:.4f}"', tmpl, count=1)
new_tag = re.sub(r">([^<]*)", f">{args.text}", new_tag, count=1)
+ if args.white:
+ new_tag = re.sub(r'fill="[^"]*"', 'fill="#ffffff"', new_tag, count=1)
out_svg = svg.replace(tmpl, new_tag, 1)
out_path = args.output or f"{args.text}.svg"