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: Logo with text on the right

+## 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 @@ + + + + anemoi + template + + + + + + + + + + + + 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"