Repository: WeblateOrg/weblate

Issue:
Add png status badge
**Is your feature request related to a problem? If so, please describe.**

On certain sites, we aren't allowed to use SVG files unless they're from trusted sources.
For my particular case, it's the [Visual Studio Code Marketplace](https://marketplace.visualstudio.com/), which disallows SVGs for security reasons. ([More Info](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#publishing-extensions))

**Describe the solution you'd like**

Could we have a separate URL that generates a PNG version of `SVGBadgeWidget`? 

Then there could be 2 versions:
* SVG - `/widgets/{project-name}/-/status-badge.svg` / `/widgets/{project-name}/-/svg-badge.svg`
* PNG - `/widgets/{project-name}/-/status-badge.png`

**Additional context**

<!--
Add any other context about the problem here.
-->

I think a nice way might be to add a utility in the `weblate.utils` module to convert an SVG to PNG in memory?
This way it's really efficient to build the image because it's just inserting variables into XML, and can be converted into the PNG as a final step.

Otherwise, it could be fine to just build the image like any of the other raster/bitmap widgets?

<details>
   <summary>The rest is only relevant if there is a way to convert an SVG with CSS/JavaScript to a PNG. I was just playing with SVGs, but had issues rasterizing them. Just noting the thoughts here.</summary>

---

I've tried looking a bit, but it seems most aren't able to handle things like `<style>` or `<foreignObject>`.
If there is a way to achieve this, then this approach could be used to resolve other issues in the repository as well.

https://github.com/WeblateOrg/weblate/issues/4198#issuecomment-662497131 - If this used an SVG base, then one can just use [getComputedTextWidth](https://developer.mozilla.org/en-US/docs/Web/API/SVGTextContentElement) to size the button according to the text.

```svg
<svg version="1.1" viewBox="0 0 512 512"
  xmlns="http://www.w3.org/2000/svg">
  <title>My Cool SVG</title>
  <desc>Base for Open Graph Thing</desc>

  <rect id="rect" rx="16" fill="grey"/>
  <text id="text" x="64" y="64" font-size="16" fill="white">Use Weblate</text>

  <script>
    const PADDING = 16;

    const rect = document.getElementById('rect');
    const text = document.getElementById('text');

    const textLength = text.getComputedTextLength();
    
    const textX = parseInt(text.getAttribute('x'));
    const textY = parseInt(text.getAttribute('y'));
    const textSize = parseInt(text.getAttribute('font-size'));

    const x = textX - PADDING;
    const y = textY - (textSize) - (PADDING / 2);
    const width = textLength + (PADDING * 2);
    const height = textSize + PADDING;

    rect.setAttribute('x', x);
    rect.setAttribute('y', y);
    rect.setAttribute('width', width);
    rect.setAttribute('height', height);
  </script>
</svg>
```

#6219 - If this used an SVG base, then one can just use CSS to truncate and use an ellipsis.

```svg
<svg version="1.1" viewBox="0 0 512 512"
  xmlns="http://www.w3.org/2000/svg">
  <title>My Cool SVG</title>
  <desc>Base for Open Graph Thing</desc>

  <style>
    div {
      width: 256px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    #project-name {
      font-weight: bold;
    }
  </style>

  <foreignObject x="0" y="0" width="512" height="512">
    <div xmlns="http://www.w3.org/1999/xhtml">
      <span id="project">Project</span>
      <span id="project-name">Very Long Project Name</span>
    </div>
  </foreignObject>
</svg>
```
</details>


Discussion:
It used to be there, but was removed years ago in 4169cf31e9d6cb4b4dfd424d7bb9199f85109c94, because of #1511
This issue has been put aside. It is currently unclear if it will ever be implemented as it seems to cover too narrow of a use case or doesn't seem to fit into Weblate.

Please try to clarify the use case or consider proposing something more generic to make it useful to more users.
If you're not certain if you'd like to include this, I can make a PR to [badges/shields](https://github.com/badges/shields) instead? ([shields.io](https://shields.io/))

I'm already looking at the API endpoints, and can make a couple of badges for total users, total projects, translation status, etc.

That would also resolve the problem this request was looking to solve, as shields.io allows both SVG and PNG badges. Plus I can just use the SVG badge anyway as shields.io is one of the trusted sources for the Visual Studio Code Marketplace.

If this suits you more, you're welcome to close this. I can also drop a comment when the badges are added to shields.io.

Edit: Created a PR in shields.io.
* https://github.com/badges/shields/pull/6677
Merged! \\(^-^)/

#### SVG
![SVG](https://img.shields.io/weblate/progress/weblate?server=https%3A%2F%2Fhosted.weblate.org)
> https://img.shields.io/weblate/progress/weblate?server=https%3A%2F%2Fhosted.weblate.org

#### PNG
![PNG](https://raster.shields.io/weblate/progress/weblate?server=https%3A%2F%2Fhosted.weblate.org)
> https://raster.shields.io/weblate/progress/weblate?server=https%3A%2F%2Fhosted.weblate.org

Resolving patch:
diff --git a/weblate/trans/views/widgets.py b/weblate/trans/views/widgets.py
--- a/weblate/trans/views/widgets.py
+++ b/weblate/trans/views/widgets.py
@@ -116,7 +116,7 @@ def get_redirect_url(
                 path.append("-")
             path.append(lang)
         # Redirect no longer supported badge styles to svg
-        if widget in {"status", "shields"}:
+        if widget == "shields":
             widget = "svg"
         return reverse(
             "widget-image",
diff --git a/weblate/trans/widgets.py b/weblate/trans/widgets.py
--- a/weblate/trans/widgets.py
+++ b/weblate/trans/widgets.py
@@ -5,6 +5,7 @@
 from __future__ import annotations
 
 import os.path
+from io import StringIO
 from typing import TYPE_CHECKING
 
 import cairo
@@ -34,8 +35,9 @@
 
 gi.require_version("PangoCairo", "1.0")
 gi.require_version("Pango", "1.0")
+gi.require_version("Rsvg", "2.0")
 
-from gi.repository import Pango, PangoCairo  # noqa: E402
+from gi.repository import Pango, PangoCairo, Rsvg  # noqa: E402
 
 COLOR_DATA = {
     "grey": (0, 0, 0),
@@ -210,6 +212,30 @@ def render(self, response) -> None:
         raise NotImplementedError
 
 
+class PNGWidget(SVGWidget):
+    extension = "png"
+    content_type = "image/png"
+
+    def render(self, response) -> None:
+        with StringIO() as output:
+            super().render(output)
+            svgdata = output.getvalue()
+
+        handle = Rsvg.Handle.new_from_data(svgdata.encode())
+        dimensions = handle.get_dimensions()
+        viewport = Rsvg.Rectangle()
+        viewport.x = 0
+        viewport.y = 0
+        viewport.width = dimensions.width
+        viewport.height = dimensions.height
+        surface = cairo.ImageSurface(
+            cairo.FORMAT_ARGB32, int(dimensions.width), int(dimensions.height)
+        )
+        context = cairo.Context(surface)
+        handle.render_document(context, viewport)
+        surface.write_to_png(response)
+
+
 @register_widget
 class NormalWidget(BitmapWidget):
     name = "287x66"
@@ -340,7 +366,7 @@ class SVGBadgeWidget(SVGWidget):
     colors: tuple[str, ...] = ("badge",)
     order = 80
     template_name = "svg/badge.svg"
-    verbose = gettext_lazy("Status badge")
+    verbose = gettext_lazy("SVG status badge")
 
     def render(self, response) -> None:
         translated_text = gettext("translated")
@@ -375,6 +401,12 @@ def render(self, response) -> None:
         )
 
 
+@register_widget
+class PNGBadgeWidget(PNGWidget, SVGBadgeWidget):
+    name = "status"
+    verbose = gettext_lazy("PNG status badge")
+
+
 @register_widget
 class MultiLanguageWidget(SVGWidget):
     name = "multi"


Test patch: