From e90871f210b5d281da5c238b9e67917fe9222585 Mon Sep 17 00:00:00 2001
From: Alexander Voss <alex@corealisation.com>
Date: Mon, 29 Apr 2024 10:04:43 +0200
Subject: [PATCH] Fixed social plugin crashing on Windows when downloading
 fonts (#7085) (#7117)

* fix: social plugin fonts on Windows (squidfunk #7085)

* fix: managed to edit in material instead of src

* added resource mgmt for ByteIO, comments

* formatted comment

* Fix for Social plugin crashes when font autoloading is disabled (#7118)
---
 material/plugins/social/plugin.py | 31 ++++++++++++++++++-------------
 src/plugins/social/plugin.py      | 31 ++++++++++++++++++-------------
 2 files changed, 36 insertions(+), 26 deletions(-)

diff --git a/material/plugins/social/plugin.py b/material/plugins/social/plugin.py
index ba914101a95..edbb91d1da8 100644
--- a/material/plugins/social/plugin.py
+++ b/material/plugins/social/plugin.py
@@ -46,7 +46,7 @@
 from mkdocs.commands.build import DuplicateFilter
 from mkdocs.exceptions import PluginError
 from mkdocs.plugins import BasePlugin
-from mkdocs.utils import copy_file
+from mkdocs.utils import write_file
 from shutil import copyfile
 from tempfile import NamedTemporaryFile
 
@@ -444,11 +444,17 @@ def _load_logo_svg(self, path, fill = None):
         svg2png(bytestring = data, write_to = file, scale = 10)
         return Image.open(file)
 
-    # Retrieve font
+    # Retrieve font either from the card layout option or from the Material
+    # font defintion. If no font is defined for Material or font is False
+    # then choose a default.
     def _load_font(self, config):
         name = self.config.cards_layout_options.get("font_family")
         if not name:
-            name = config.theme.get("font", {}).get("text", "Roboto")
+            material_name = config.theme.get("font", False)
+            if material_name is False:
+                name = "Roboto"
+            else:
+                name = material_name.get("text", "Roboto")
 
         # Resolve relevant fonts
         font = {}
@@ -522,19 +528,18 @@ def _fetch_font_from_google_fonts(self, family: str):
             with requests.get(match) as res:
                 res.raise_for_status()
 
-                # Create a temporary file to download the font
-                with NamedTemporaryFile() as temp:
-                    temp.write(res.content)
-                    temp.flush()
-
-                    # Extract font family name and style
-                    font = ImageFont.truetype(temp.name)
+                # Extract font family name and style using the content in the
+                # response via ByteIO to avoid writing a temp file. Done to fix
+                # problems with passing a NamedTemporaryFile to
+                # ImageFont.truetype() on Windows, see https://t.ly/LiF_k
+                with BytesIO(res.content) as fontdata:
+                    font = ImageFont.truetype(fontdata)
                     name, style = font.getname()
                     name = " ".join([name.replace(family, ""), style]).strip()
-
-                    # Move fonts to cache directory
                     target = os.path.join(path, family, f"{name}.ttf")
-                    copy_file(temp.name, target)
+
+                # write file to cache
+                write_file(res.content, target)
 
 # -----------------------------------------------------------------------------
 # Data
diff --git a/src/plugins/social/plugin.py b/src/plugins/social/plugin.py
index ba914101a95..edbb91d1da8 100644
--- a/src/plugins/social/plugin.py
+++ b/src/plugins/social/plugin.py
@@ -46,7 +46,7 @@
 from mkdocs.commands.build import DuplicateFilter
 from mkdocs.exceptions import PluginError
 from mkdocs.plugins import BasePlugin
-from mkdocs.utils import copy_file
+from mkdocs.utils import write_file
 from shutil import copyfile
 from tempfile import NamedTemporaryFile
 
@@ -444,11 +444,17 @@ def _load_logo_svg(self, path, fill = None):
         svg2png(bytestring = data, write_to = file, scale = 10)
         return Image.open(file)
 
-    # Retrieve font
+    # Retrieve font either from the card layout option or from the Material
+    # font defintion. If no font is defined for Material or font is False
+    # then choose a default.
     def _load_font(self, config):
         name = self.config.cards_layout_options.get("font_family")
         if not name:
-            name = config.theme.get("font", {}).get("text", "Roboto")
+            material_name = config.theme.get("font", False)
+            if material_name is False:
+                name = "Roboto"
+            else:
+                name = material_name.get("text", "Roboto")
 
         # Resolve relevant fonts
         font = {}
@@ -522,19 +528,18 @@ def _fetch_font_from_google_fonts(self, family: str):
             with requests.get(match) as res:
                 res.raise_for_status()
 
-                # Create a temporary file to download the font
-                with NamedTemporaryFile() as temp:
-                    temp.write(res.content)
-                    temp.flush()
-
-                    # Extract font family name and style
-                    font = ImageFont.truetype(temp.name)
+                # Extract font family name and style using the content in the
+                # response via ByteIO to avoid writing a temp file. Done to fix
+                # problems with passing a NamedTemporaryFile to
+                # ImageFont.truetype() on Windows, see https://t.ly/LiF_k
+                with BytesIO(res.content) as fontdata:
+                    font = ImageFont.truetype(fontdata)
                     name, style = font.getname()
                     name = " ".join([name.replace(family, ""), style]).strip()
-
-                    # Move fonts to cache directory
                     target = os.path.join(path, family, f"{name}.ttf")
-                    copy_file(temp.name, target)
+
+                # write file to cache
+                write_file(res.content, target)
 
 # -----------------------------------------------------------------------------
 # Data